Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 2:02cb20446785, committed 2017-01-24
- Comitter:
- davidjhoward
- Date:
- Tue Jan 24 21:12:26 2017 +0000
- Parent:
- 1:b2e90cda7a5a
- Commit message:
- Delete ICE-Application
Changed in this revision
--- a/ICE-Application/inc/global.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,258 +0,0 @@ -#ifndef GLOBAL_H -#define GLOBAL_H - -#include <map> - -#define LOAD_PERSISTENT_CONFIGURATIONS -#include "mbed.h" -#include "ntshell.h" -#include "rtos.h" -#include "ConfigFs.h" - -extern int sig_output_continue; -extern int sig_config_continue; -extern int sig_control_continue; - -extern osThreadId mainThreadId; - -extern Thread *GLOBAL_analyticsLogger_thread; -extern Thread *GLOBAL_modbusMaster_thread; -extern Thread *GLOBAL_BLE_thread; -extern Thread *GLOBAL_CDH_thread; -extern Thread *GLOBAL_configHandler_thread; -extern Thread *GLOBAL_controlTask_thread; -extern Thread *GLOBAL_outputTask_thread; -extern Thread *GLOBAL_dataHandler_thread; - -extern ConfigFs *GLOBAL_mdot; -extern I2C i2c_instance; -extern I2C* i2c; - -#define EXECUTE_SCRIPT - -// declare stack sizes -#ifdef EXECUTE_SCRIPT -#define MODBUS_MASTER_STACK_SIZE (1024 * 8) -#else -#define MODBUS_MASTER_STACK_SIZE (1024 * 4) -#endif -#define OUTPUT_TASK_STACK_SIZE ((1024 * 2) + 512) -#define CONFIG_HANDLER_STACK_SIZE (1024 * 3) -#define CONTROL_TASK_STACK_SIZE (1024 * 2) -#ifdef MDOT_ICE -#define BLE_DATA_HANDLER_STACK_SIZE (1024 * 3) -#define CLOUD_DATA_HANDLER_STACK_SIZE (1024 * 4) -#else -#define DATA_HANDLER_STACK_SIZE (1024 * 4) -#endif -#define ANALYTICS_LOGGER_STACK_SIZE (1024 * 2) - -/* tiny shell specifics */ -extern ntshell_t ntshell; - -extern int func_read(char *buf, int cnt); -extern int func_write(const char *buf, int cnt); -extern int func_cb_ntshell(const char *text); -extern void func_cb_ntopt(int argc, char **argv); - -#define MAX_FILE_SIZE 350 - -/**************************************************************************** - * Mailbox messaging data structures - ****************************************************************************/ - -#define CONTROL_FILE_SIZE (64) -#define MODBUS_MSG_SIZE (256) - -// Messages sent to the configuration handler ------------------------------- - -typedef enum action_tag { - ACTION_CREATE, - ACTION_MODIFY, - ACTION_DESTROY, - ACTION_READ_FILE, - ACTION_EXEC_CMD -} Action_t; - -typedef enum control_tag { - CONTROL_TIMER = 0, - CONTROL_PID = 1, - CONTROL_SETPOINT = 2, - CONTROL_COMPOSITE = 3, - CONTROL_MANUAL = 4, - CONTROL_FAILSAFE = 5, - CONTROL_SEQUENCE = 6, - CONTROL_SENSOR_ERROR = 7, - CONTROL_ALGORITHM = 8 -} Control_t; - -typedef struct message_tag { - Action_t action; - Control_t control; - char controlFile[CONTROL_FILE_SIZE]; -} ConfigMessage_t; - -// Messages sent to the Modbus Master --------------------------------------- - -typedef enum thread_name_tag { - ANALYTICS_LOGGER = 0, - BLE_HANDLER = 1, - CLOUD_DATA_HANDLER = 2, - CONFIG_HANDLER = 3, - CONTROL_TASK = 4, - OUTPUT_TASK = 5, -} ThreadName_t; - -typedef struct modbus_master_tag { - Action_t action; - Control_t control; - ThreadName_t replyThread; - char msg[MODBUS_MSG_SIZE]; -} ModbusMasterReq_t; - -// Messages sent to the Output Task ------------------------------------------ - -typedef enum output_action { - ACTION_NEW, - ACTION_CONTROL_ON, - ACTION_CONTROL_OFF, - ACTION_CONTROL_REGISTER, - ACTION_CONTROL_UNREGISTER -} OutputAction; - -typedef struct output_control_req_t { - OutputAction action; - Control_t controlType; - char controlFile[CONTROL_FILE_SIZE]; - char input_tag[32]; - char output_tag[32]; - char id[32]; - unsigned int priority; -} OutputControlMsg_t; - - -// Messages sent to the Analytics Logger ------------------------------------ - -typedef struct analytics_logger_req_t { - char timestamp[32]; - char log_entry[96]; -} AnalyticsLoggerReq_t; - -// BLE request message -typedef struct ble_handler_req_t { - char reply[500]; -} BLEHandlerReq_t; - -extern Mail<ConfigMessage_t, 16> ConfigHandlerMailBox; -extern Mail<ModbusMasterReq_t, 2> ModbusMasterMailBox; -extern Mail<OutputControlMsg_t, 16> OutputMasterMailBox; -extern Mail<BLEHandlerReq_t, 1> BLEHandlerMailBox; - -/**************************************************************************** - * MODBUS - * TODO! Move this to the proper location - ****************************************************************************/ - -#define NUM_SCRIPT_ARGS 4 - -typedef enum register_type_tag { - REG_TYPE_NONE, - REG_TYPE_INPUT, - REG_TYPE_OUTPUT, - REG_TYPE_VINPUT, - REG_TYPE_VOUTPUT, -} RegisterType_t; - -#define MODBUS_READ_COIL 1 -#define MODBUS_READ_DISCRETE_INPUT 2 -#define MODBUS_READ_HOLDING 3 -#define MODBUS_READ_INPUT 4 -#define MODBUS_WRITE_SINGLE_COIL 5 -#define MODBUS_WRITE_SINGLE_HOLDING 6 -#define MODBUS_WRITE_MULTIPLE_COIL 15 -#define MODBUS_WRITE_MULTIPLE_HOLDING 16 - -#define ARGT_INT 0 -#define ARGT_FLOAT 1 -#define ARGT_DOUBLE 2 -#define ARGT_STRING 3 - -struct ModbusRegister { - float min; - float max; - unsigned char node; - unsigned char rtype; - unsigned char type; - unsigned char size; - unsigned char order; - unsigned char rfreq; - unsigned char argc; - uint32_t reg; - RegisterType_t regType; - std::string cmd; - std::string argv[NUM_SCRIPT_ARGS]; - bool simulated; -}; - -struct ExecuteJavaScript { - std::string script; - std::string argv[NUM_SCRIPT_ARGS]; -}; - -struct RegisterValue { - double float_value; - uint32_t errflag; -}; - -struct VirtualCommand { - float Constant; - std::string Operand; - std::string Operator; -}; - -struct HoldingRegister { - unsigned char node; - unsigned char sreg; - unsigned char nreg; - unsigned char order; -}; - -struct SimulateInput { - float start_value; - float min; - float max; - float up_step; - float down_step; - bool moving_up; - uint32_t errflag; -}; - -typedef enum byte_order { - BigEndian = 0, - LittleEndian = 1, - BigEndianReverseWord = 2, - LittleEndianReversWord = 3, -} Byte_Order; - -typedef enum event_reason_tag { - EVENT_REASON_AUTO, - EVENT_REASON_MANUAL, - EVENT_REASON_TIMER, - EVENT_REASON_FLOW, - EVENT_REASON_FAILSAFE, - EVENT_REASON_NO_CONTROL -} EventReason_t; - -typedef struct event_reason_struct_t { - EventReason_t eventReason; - char inputTag[32]; - char outputTag[32]; - float inputValue; - float outputValue; -} EventReasonStruct_t; - -extern std::map<std::string, ModbusRegister> ModbusRegisterMap; -extern std::map<std::string, RegisterValue> RegisterValueMap; -extern std::map<std::string, SimulateInput> SimulateInputMap; - -#endif
--- a/ICE-Application/inc/version.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -/****************************************************************************** - * version.h - *****************************************************************************/ -#ifndef VERSION_H -#define VERSION_H - -#define MAJOR_VERSION_NUMBER 0 -#define MINOR_VERSION_NUMBER 1 -#define PATCH_VERSION_NUMBER 0 - -#define PROJECT_CODE_NAME "foobar" - -#endif
--- a/ICE-Application/main.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,213 +0,0 @@ -/****************************************************************************** - * - * File: main.cpp - * Desciption: Ground Zero - * - *****************************************************************************/ -#include <time.h> -#include <iostream> - -#include "global.h" -#include "mbed.h" -#include "rtos.h" -#include "ntshell.h" -#include "version.h" -#include "AnalyticsLogger.h" -#include "ConfigurationHandler.h" -#include "ControlTask.h" -#include "DataHandler.h" -#include "ModbusMaster.h" -#include "OutputTask.h" -#include "ConfigFs.h" -#include "rtc.h" -#include "mfs.h" - -// threading data -osThreadId mainThreadId = NULL; - -Thread *GLOBAL_analyticsLogger_thread = NULL; -Thread *GLOBAL_modbusMaster_thread = NULL; -Thread *GLOBAL_BLE_thread = NULL; -Thread *GLOBAL_CDH_thread = NULL; -Thread *GLOBAL_configHandler_thread = NULL; -Thread *GLOBAL_controlTask_thread = NULL; -Thread *GLOBAL_outputTask_thread = NULL; -Thread *GLOBAL_dataHandler_thread = NULL; - -// mailboxes -Mail<ConfigMessage_t, 16> ConfigHandlerMailBox; -Mail<ModbusMasterReq_t, 2> ModbusMasterMailBox; -Mail<OutputControlMsg_t, 16> OutputMasterMailBox; -Mail<AnalyticsLoggerReq_t, 16> AnalyticsLoggerMailBox; -Mail<BLEHandlerReq_t, 1> BLEHandlerMailBox; - -// bootup sequence signals -int sig_output_continue = 0x1; // allow the output handler to start -int sig_config_continue = 0x2; // allow the config handler to start -int sig_control_continue = 0x3; // allow the control task to start - -// some digitals...for fun, whilst developing -DigitalOut led1(LED1); -DigitalOut led2(LED2); -DigitalOut led3(LED3); - -DigitalIn rClk(D7); - -// console communication settings -Serial console(USBTX, USBRX, 115200); - -I2C* i2c; - -ConfigFs EEP_FileSystem; -ConfigFs *GLOBAL_mdot = &EEP_FileSystem; - -//MTSLog *GLOBAL_logger; - -#undef TODO_ICE // move this, doesn't belong here... -// store modbus register information -std::map<std::string,ModbusRegister> ModbusRegisterMap; -std::map<std::string,RegisterValue> RegisterValueMap; -std::map<std::string,SimulateInput> SimulateInputMap; - -// local prototypes -static void banner(void); - -/***************************************************************************** - * Function: timestamp_boot_record() - * Description: timestamp the boot record with current time - * - * @param none - * @return none - *****************************************************************************/ -static void timestamp_boot_record(void) -{ - char time_string[80]; - time_t curr_sec; - struct tm *ts; - - curr_sec = time(0); - ts = localtime(&curr_sec); - strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", ts); - - GLOBAL_mdot->saveUserFile("boot.time", time_string, sizeof(time_string)); - return; -} - - -/** - * @brief the main software entry point - * - * @param none - * @return 0 - */ - - - -int main(void) -{ - // light 'em up! -- remove this once we go to a real board! - led1 = led2 = led3 = 1; - - struct tm rtc_time; - time_t curr_sec; - int year = 0; - - mainThreadId = osThreadGetId(); - - i2c = &i2c_instance; - - // intialize the real-time clock - rtc_init(); - - rtc_get_time(&year, &rtc_time.tm_mon, &rtc_time.tm_mday, &rtc_time.tm_hour, &rtc_time.tm_min, &rtc_time.tm_sec); - rtc_time.tm_mon = rtc_time.tm_mon - 1; - rtc_time.tm_year = year - 1900; - curr_sec = mktime( &rtc_time ); - - set_time(curr_sec); - - timestamp_boot_record(); - - mainThreadId = osThreadGetId(); - - // display the startup banner - banner(); - - // the Modbus master task - Thread modbusMaster_thread(ModbusMaster, NULL, osPriorityHigh, MODBUS_MASTER_STACK_SIZE, NULL); - osSignalWait(sig_output_continue, osWaitForever); - - // the Output task - Thread outputTask_thread(OutputTask, NULL, osPriorityNormal, OUTPUT_TASK_STACK_SIZE, NULL); - osSignalWait(sig_config_continue, osWaitForever); - - // the Configuration Handler task - Thread configHandler_thread(ConfigurationHandler, NULL, osPriorityNormal, CONFIG_HANDLER_STACK_SIZE, NULL); - osSignalWait(sig_control_continue, osWaitForever); - - // the Control task - Thread controlTask_thread(ControlTask, NULL, osPriorityNormal, CONTROL_TASK_STACK_SIZE, NULL); - - // the Data Handler - Thread dataHandler_thread(DataHandler, NULL, osPriorityNormal, DATA_HANDLER_STACK_SIZE, NULL); - - // the Analytics Logger task - Thread analyticsLoggerThread(AnalyticsLogger, NULL, osPriorityHigh, ANALYTICS_LOGGER_STACK_SIZE, NULL); - - GLOBAL_analyticsLogger_thread = &analyticsLoggerThread; - GLOBAL_modbusMaster_thread = &modbusMaster_thread; - GLOBAL_configHandler_thread = &configHandler_thread; - GLOBAL_controlTask_thread = &controlTask_thread; - GLOBAL_outputTask_thread = &outputTask_thread; - GLOBAL_dataHandler_thread = &dataHandler_thread; - - Thread::wait(2500); - - // start the command shell -- this will loop forever - console.printf("\r\nInvoking the command shell\n\r\n"); - ntshell_execute(&ntshell, func_read, func_write, func_cb_ntshell); -} - -/** - * @brief the bootup banner - * - * @param none - * @return none - */ -static void banner(void) -{ - struct tm *ts; - time_t curr_sec; - char time_string[80]; - - curr_sec = time(0); - ts = localtime(&curr_sec); - - strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", ts); - - printf("\n\n\r\nProject: ICE v%d.%d.%d (%s)\n", - MAJOR_VERSION_NUMBER, - MINOR_VERSION_NUMBER, - PATCH_VERSION_NUMBER, - PROJECT_CODE_NAME); - - printf("\rThe Intelligent Connected Experience\n"); - printf("\rCopyright 2017 Nalco Water, an Ecolab Company\n"); - -#ifdef __ICCARM__ - printf("\rToolchain: IAR\n"); -#else - printf("\rToolchain: MBED\n"); -#endif - - printf("\r _________ _______ _______ \n"); - printf("\r \\__ __/( ____ \\( ____ \\ \n"); - printf("\r ) ( | ( \\/| ( \\/ \n"); - printf("\r | | | | | (__ \n"); - printf("\r | | | | | __) \n"); - printf("\r | | | | | ( \n"); - printf("\r ___) (___| (____/\\| (____/\\ \n"); - printf("\r \\_______/(_______/(_______/ \n"); - - printf("\r\nCurrent time is: %s\r\n", time_string); -}
--- a/ICE-Application/src/AnalyticsLogger/AnalyticsLogger.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -/****************************************************************************** - * - * File: AnalyticsLogger.cpp - * Desciption: source for the ICE Analytics Logger - * - *****************************************************************************/ -#include "global.h" -#include <stdio.h> -#include <string> -#include <sstream> -#include <iostream> -#include <vector> -#include <time.h> -#include "AnalyticsLogger.h" -#include "LoggerApi.h" -//#include "rtc.h" - -static void checkStackUsage(void); - -/***************************************************************************** - * Function: AnalyticsLogger - * Description: entry point for the Analytics Logger - * - * @param (IN) args (user-defined arguments) - * @return none - *****************************************************************************/ -void AnalyticsLogger(void const *args) -{ - struct tm *ts; - time_t curr_sec; - int last_min=8; - bool log_sent=false; - - printf("\rAnalyticsLogger has started...\n"); - - while ( true ) { - - std::ostringstream log_event; - - curr_sec = time(0); - ts = localtime(&curr_sec); -// printf("curr_sec=%ld, min=%d (last=%d)\r\n", curr_sec, ts->tm_min, last_min ); - if( ((ts->tm_min%5) == 0) && (ts->tm_min != last_min) ) { - last_min = ts->tm_min; - - int map_count = ModbusRegisterMap.size(); - int collected_count = 0; - - log_event << "\"lr\":["; - std::map<std::string, ModbusRegister>::iterator iter; - for (iter = ModbusRegisterMap.begin(); iter != ModbusRegisterMap.end(); ++iter) { - - collected_count = collected_count + 1; - - log_event << "{\"t\":"<< "\"" << iter->first.c_str() << "\"," << "\"v\":"<< "\"" << RegisterValueMap[iter->first].float_value<< "\"},"; - log_sent = false; - if( log_event.str().size() >= 150 ) { - std::string str = log_event.str(); - str.erase( str.size() - 1 ); - if( collected_count == map_count ) { - str.append("],\"seq\":\"0\""); - } else { - str.append("],\"seq\":\"1\""); - } -// printf("%s:%d: Logging %s : len=%d\r\n", __func__, __LINE__, str.c_str(), str.length() ); - LiveDataLoggerApi( str.c_str() ); - log_event.str(""); - log_event.clear(); - log_event << "\"lr\":["; - log_sent = true; - } - } - if( log_sent == false ) { - std::string str = log_event.str(); - str.erase( str.size() - 1 ); - str.append("],\"seq\":\"0\""); - if( str.length() > 20 ) { -// printf("%s:%d: Logging %s : len=%d\r\n", __func__, __LINE__, str.c_str(), str.length() ); - LiveDataLoggerApi( str.c_str() ); - } - } - } - - Thread::wait(5000); - checkStackUsage(); - } -} - -// -// For optimizing/debugging purposes only! -// -static void checkStackUsage(void) -{ - const float threshold = .85; - - std::vector<std::pair<std::string, Thread*> > taskList; - taskList.push_back(make_pair((std::string)"AnalyticsLogger", GLOBAL_analyticsLogger_thread)); - taskList.push_back(make_pair((std::string)"DataHandler", GLOBAL_dataHandler_thread)); - taskList.push_back(make_pair((std::string)"ConfigHandler", GLOBAL_configHandler_thread)); - taskList.push_back(make_pair((std::string)"ControlTask", GLOBAL_controlTask_thread)); - taskList.push_back(make_pair((std::string)"ModbusMaster", GLOBAL_modbusMaster_thread)); - taskList.push_back(make_pair((std::string)"OutputTask", GLOBAL_outputTask_thread)); - for ( std::vector<std::pair<std::string, Thread*> >::iterator pos = taskList.begin(); pos != taskList.end(); ++ pos) { - // do something - if ( (float) pos->second->max_stack() / (float) pos->second->stack_size() >= threshold ) { - printf("\rWARNING: %s has used %.2f%% of its stack!\n", pos->first.c_str(), - (double)((float)pos->second->max_stack() / (float)pos->second->stack_size()) * 100.0); - } - } -} \ No newline at end of file
--- a/ICE-Application/src/AnalyticsLogger/AnalyticsLogger.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -/****************************************************************************** - * - * File: AnalyticsLogger.h - * Desciption: interface for the ICE Analytics Logger - * - *****************************************************************************/ -#ifndef ANALYTICS_LOGGER_H -#define ANALYTICS_LOGGER_H - -#ifdef __cplusplus -#define EXTERNC extern "C" -#else -#define EXTERNC -#endif - -EXTERNC void AnalyticsLogger ( void const *args ); -EXTERNC bool AnalyticsLoggerApi( std::string *log_string ); - -#undef EXTERNC - -#endif \ No newline at end of file
--- a/ICE-Application/src/BLEDataHandler/BLEDataHandler.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,303 +0,0 @@ -/****************************************************************************** - * - * File: BLEDataHandler.cpp - * Desciption: source for the ICE Bluetooth Low Energy Data Handler - * - *****************************************************************************/ -#include "global.h" -#include <stdio.h> -#include <sstream> -#include <iostream> -#include "BLEDataHandler.h" -#include "CloudDataHandler.h" -#include "LogLocalApi.h" -#include "LoggerApi.h" -#include "ble_main.h" -#include "ble_init.h" -#include "ble_msg_handler.h" -#include "ble_spi.h" -#include "cJSON.h" - -/***************************************************************************** - * Function: BLEDataHandler - * Description: entry point for the Analytics Logger - * - * @param (IN) args (user-defined arguments) - * @return none - *****************************************************************************/ - -BLE_FILE BLE; -BLE_INIT ble_init; - -#define BLE_GAP_DEVNAME_MAX_LEN 31 //max length is 31including null -#define BLE_RECEIVE_BUFFER_SIZE 500 -#define BLE_TRANSMIT_BUFFER_SIZE 500 -//const static char BLE_DEVICE_NAME[BLE_GAP_DEVNAME_MAX_LEN] = {"ICE-Ecolab"}; - -bool BLE_StringReceived = false; -char BLE_ReceiveString[BLE_RECEIVE_BUFFER_SIZE]; - -void BLE_ReplyToHandler( std::string &id, int status, float value ) -{ - std::ostringstream mb_reply; - - BLEHandlerReq_t *mail = BLEHandlerMailBox.alloc(); - mb_reply << "{ \"mtype\":" << BT_MODBUS_COMMAND_REPLY_MTYPE << ", \"mbreply\":{ \"id\":\"" << id.c_str() << "\"," "\"status\":\"" << status << "\"," "\"value\":\"" << value << "\"} }"; - std::string string_reply = mb_reply.str(); - strncpy( mail->reply, string_reply.c_str(), (sizeof(mail->reply)-1)); - printf("%s:%d: Reply is: %s\r\n",__func__,__LINE__, mail->reply); - BLEHandlerMailBox.put(mail); -} - -/***************************************************************************** -* Function: BLE data receive callback -* Description: Process BLE data -* -* @param spi_rcv_array -* @param *rx_data -* @param length -* @return none -*****************************************************************************/ -//char * fake_string = "{ \"mtype\":1200, \"getreadings\":[ { \"tag\":\"i_tra01\" } ] }"; // use this to fake a message out requesting i_tra01 readings -//char * fake_string = "{ \"mtype\":1200, \"getreadings\":[ { \"tag\":\"i_tra01\" }, { \"tag\":\"i_bdcond01\" }, { \"tag\":\"i_cond_temp01\" } ] }"; // use this to fake a message out requesting i_tra01 readings -//char * fake_string = "{ \"mtype\":1000, \"mbcommand\":{ \"id\":\"READ_TRASAR\"," "\"node\":\"1\"," "\"func\":\"4\"," "\"sreg\":\"9\"," "\"nreg\":\"2\"," "\"dtype\":\"0\"," "\"order\":\"2\"," "\"value\":\"0\" } }"; // use this to fake a message out requesting i_tra01 readings -//char * fake_string = "{ \"mtype\":1000, \"mbcommand\":{ \"id\":\"WRITE_MULTIPLE\"," "\"node\":\"21\"," "\"func\":\"16\"," "\"sreg\":\"0\"," "\"nreg\":\"3\"," "\"dtype\":\"5\"," "\"order\":\"2\"," "\"value\":\"0\", \"data\":[ { \"v\":\"20\" }, { \"v\":\"16\" }, { \"v\":\"0\" } ] } }"; // use this to fake a message out requesting i_tra01 readings -//char * fake_string = "{ \"mtype\":103, \"mncontrol\": { \"id\":\"o_rly01\", \"output\":\"o_rly01\", \"type\":\"1\", \"priority\":\"100\", \"duration\":\"0\", \"setpoint\":\"0\", \"state\":\"1\", \"percent\":\"100\" } }"; -//char * fake_string = "{ \"mtype\":100, \"spcontrol\": { \"id\": \"INH_TRA_01\", \"priority\": \"800\", \"input\": \"i_tra01\", \"output\": \"o_rly02\", \"setpoint\": \"130\", \"prodfact\": \"100\", \"actingDir\": \"0\", \"halert\": \"190\", \"lalert\": \"92\", \"hfs\": \"200\", \"lfs\": \"70\", \"tol\": \"4\" } }"; -static void BleRxDataCallback(uint8_t *rx_data, uint8_t len) -{ - memset( BLE_ReceiveString, 0, BLE_RECEIVE_BUFFER_SIZE ); - if( len >= BLE_RECEIVE_BUFFER_SIZE ) { - printf("Received file larger than buffer (len=%d)\r\n", len ); - return; - } -#if 0 - std::string fake_string; - switch( rx_data[0] ) { - case 'a': - fake_string.assign("{ \"mtype\":200, \"input\": { \"id\": \"i_tra01\", \"name\": \"Trasar\", \"units\": \"PPM\", \"min\": \"0\", \"max\": \"300\", \"node\": \"1\", \"reg\": \"9\", \"rtype\": \"4\", \"type\": \"0\", \"size\": \"2\", \"order\": \"2\", \"fmt\": \"%.2f\", \"rfreq\": \"5\", \"cmd\":\"\" } }"); - break; - case 'b': - fake_string.assign("{ \"mtype\":201, \"output\": { \"id\": \"o_rly02\", \"name\": \"3DTS86\", \"units\": \"\", \"min\": \"0\", \"max\": \"300\", \"node\": \"0\", \"reg\": \"2\", \"rtype\": \"1\", \"type\": \"0\", \"size\": \"2\", \"order\": \"2\", \"fmt\": \"%.2f\", \"rfreq\": \"5\", \"toperiod\":\"0\", \"scalelo\":\"0\", \"scalehi\":\"100\", \"cmd\":\"\" } }"); - break; - case 'c': - fake_string.assign("{ \"mtype\":100, \"spcontrol\": { \"id\": \"INH_TRA_01\", \"priority\": \"800\", \"input\": \"i_tra01\", \"output\": \"o_rly02\", \"setpoint\": \"130\", \"prodfact\": \"100\", \"actingDir\": \"0\", \"halert\": \"190\", \"lalert\": \"92\", \"hfs\": \"200\", \"lfs\": \"70\", \"tol\": \"4\" } }"); - break; - case 'd': - fake_string.assign("{ \"mtype\":200, \"input\": { \"id\": \"i_bdcond01\", \"name\": \"Tower Conductivity\", \"units\": \"uS\", \"min\": \"0\", \"max\": \"600\", \"node\": \"21\", \"reg\": \"18\", \"rtype\": \"4\", \"type\": \"1\", \"size\": \"2\", \"order\": \"2\", \"fmt\": \"%.2f\", \"rfreq\": \"5\", \"cmd\":\"\" } }"); - break; - case 'e': - fake_string.assign("{ \"mtype\":201, \"output\": { \"id\": \"o_rly01\", \"name\": \"Blowdown\", \"units\": \"\", \"min\": \"0\", \"max\": \"300\", \"node\": \"0\", \"reg\": \"1\", \"rtype\": \"1\", \"type\": \"0\", \"size\": \"2\", \"order\": \"2\", \"fmt\": \"%.2f\", \"rfreq\": \"5\", \"toperiod\":\"0\", \"scalelo\":\"0\", \"scalehi\":\"100\", \"cmd\":\"\" } }"); - break; - case 'f': - fake_string.assign("{ \"mtype\":100, \"spcontrol\": { \"id\": \"BLOWDOWN_01\", \"priority\": \"800\", \"input\": \"i_bdcond01\", \"output\": \"o_rly01\", \"setpoint\": \"1800\", \"prodfact\": \"\", \"actingDir\": \"1\", \"halert\": \"2400\", \"lalert\": \"800\", \"hfs\": \"2700\", \"lfs\": \"700\", \"tol\": \"30\" } }"); - break; - case 'g': - // relay-1: manual control off - fake_string.assign("{ \"mtype\":103, \"mncontrol\": { \"id\":\"o_rly01\", \"output\":\"o_rly01\", \"type\":\"1\", \"priority\":\"100\", \"duration\":\"0\", \"setpoint\":\"0\", \"state\":\"0\", \"percent\":\"100\" } }"); - break; - case 'h': - // relay-1: manual control on - fake_string.assign("{ \"mtype\":103, \"mncontrol\": { \"id\":\"o_rly01\", \"output\":\"o_rly01\", \"type\":\"1\", \"priority\":\"100\", \"duration\":\"0\", \"setpoint\":\"0\", \"state\":\"1\", \"percent\":\"100\" } }"); - break; - case 'i': - // relay-1: manual control destroy - fake_string.assign("{ \"mtype\":303, \"mncontrol\": { \"id\":\"o_rly01\" } }"); - break; - case 'j': - // relay-2: manual control off - fake_string.assign("{ \"mtype\":103, \"mncontrol\": { \"id\":\"o_rly02\", \"output\":\"o_rly02\", \"type\":\"1\", \"priority\":\"100\", \"duration\":\"0\", \"setpoint\":\"0\", \"state\":\"0\", \"percent\":\"100\" } }"); - break; - case 'k': - // relay-2: manual control on - fake_string.assign("{ \"mtype\":103, \"mncontrol\": { \"id\":\"o_rly02\", \"output\":\"o_rly02\", \"type\":\"1\", \"priority\":\"100\", \"duration\":\"0\", \"setpoint\":\"0\", \"state\":\"1\", \"percent\":\"100\" } }"); - break; - case 'l': - // relay-2: manual control destroy - fake_string.assign("{ \"mtype\":303, \"mncontrol\": { \"id\":\"o_rly02\" } }"); - break; - } -#endif - memcpy( BLE_ReceiveString, rx_data, len ); // comment out and use line below until we receive JSON from APP - // temporary until we receive actual string from APP -// memcpy( BLE_ReceiveString, fake_string.c_str(), fake_string.size() ); - BLE_StringReceived = true; -// printf("Data Received: %s\r\n", BLE_ReceiveString); -} - -void BLEDataHandler(void const *args) -{ -// uint8_t tx_array[BLE_TRANSMIT_BUFFER_SIZE]; -// uint8_t event_status; - - printf("\rBLEDataHandler has started...\n"); - - std::string id = "Ecolab"; - - std::vector<std::string>::iterator file; - std::vector<std::string> file_list; - file_list = GLOBAL_mdot->listUserFiles(); - for(file = file_list.begin(); file != file_list.end(); ++file) { - if( (strncmp( file->c_str(), "ble_device_name", (strlen("ble_device_name")-1)) == 0) ) { - bool status = GLOBAL_mdot->readUserFile(file->c_str(), BLE_ReceiveString, MAX_FILE_SIZE); - if( status != true ) { - printf("%s:%d: read file failed, file=%s, status=%d\r\n", __func__, __LINE__, file->c_str(), status); - continue; - } - - cJSON * root = cJSON_Parse(BLE_ReceiveString); - id.erase (id.begin(), id.end()); - id.append(cJSON_GetObjectItem(root,"id")->valuestring); - printf("Loaded BLE_DEVICE_NAME=%s\r\n", id.c_str()); - cJSON_Delete(root); - break; - } - } - - - /*TODO - Getting the init status from Nano BLE register and - Proceed based on the status. - */ - printf("\r\nSetting BLE_DEVICE_NAME=%s\r\n\r\n", id.c_str() ); -// event_status = BLE.ConfigureBLEDevice(id.c_str()); -// BleDataRxCbRegister(BleRxDataCallback); - while(1) { -#if 0 - event_status = PollBLEEvents(); - if(event_status == true && BLE_StringReceived == true) { - - cJSON * root = cJSON_Parse( BLE_ReceiveString ); - int mType = cJSON_GetObjectItem(root,"mtype")->valueint; - - printf("mType=%d, str=%s\r\n",mType, BLE_ReceiveString); - switch( mType ) { - case BT_GETLIVE_COMMAND_MTYPE: { - std::vector<std::string> RequestedTags; - cJSON *getreadings = cJSON_GetObjectItem(root, "getreadings"); - for ( int i = 0; i < cJSON_GetArraySize(getreadings); ++i ) { - cJSON *item = cJSON_GetArrayItem(getreadings, i); - std::string tag = cJSON_GetObjectItem(item, "tag")->valuestring; - RequestedTags.push_back(tag); - } - GetTagReadings( RequestedTags, (char *)tx_array, sizeof(tx_array) ); - printf("%s:%d: Sending Reply to APP: %s\r\n",__func__,__LINE__,tx_array); - BLE.SendFile(tx_array,strlen((char *)tx_array)); - break; - } - case BT_MODBUS_HOLD_COMMAND_MTYPE: - case BT_MODBUS_RAW_COMMAND_MTYPE: { - printf("%s:%d: GOT MODBUS COMMAND\r\n",__func__,__LINE__); - - cJSON *mbcommand = cJSON_GetObjectItem(root, "mbcommand"); - std::string payload_string = cJSON_PrintUnformatted(mbcommand); - - printf("%s:%d: Sending Command Request to ModbusMaster: %s\r\n",__func__,__LINE__,payload_string.c_str()); - - ModbusMasterReq_t *mail = ModbusMasterMailBox.alloc(); - mail->action = ACTION_EXEC_CMD; - mail->replyThread = BLE_HANDLER; - strncpy( mail->msg, payload_string.c_str(), (sizeof(mail->msg)-1)); - ModbusMasterMailBox.put(mail); - break; - } - case BT_GETLOG_COMMAND_MTYPE: { - bool log_in_eeprom; - log_in_eeprom = LogLocalApi_PopEntry( (char *)tx_array ); - if( log_in_eeprom == true ) { - printf("%s:%d: Sending Reply to APP: %s\r\n",__func__,__LINE__,tx_array); - BLE.SendFile(tx_array,strlen((char *)tx_array)); - } else { - std::ostringstream reply_oss; - reply_oss << "{ \"mtype\":" << BT_GETLOG_COMMAND_REPLY_MTYPE << ", \"log_empty\":\"true\" }"; - std::string string_reply = reply_oss.str(); - strncpy( (char *)tx_array, string_reply.c_str(), (sizeof(tx_array)-1)); - printf("%s:%d: Sending Reply to APP: %s\r\n",__func__,__LINE__, tx_array); - BLE.SendFile(tx_array,strlen((char *)tx_array)); - } - break; - } - case MANUAL_CONTROL_MTYPE: - case DESTROY_MANUAL_MTYPE: { - - printf("%s:%d: Invoke Manual Control COMMAND VIA BLE\r\n", __func__,__LINE__); - std::string json_string = BLE_ReceiveString; - bool status = !StoreReceivedFile( json_string ); - - std::ostringstream reply_oss; - cJSON *mncontrol = cJSON_GetObjectItem(root, "mncontrol"); - std::string id = cJSON_GetObjectItem(mncontrol, "id")->valuestring; - reply_oss << "{ \"mtype\":" << MANUAL_CONTROL_REPLY_MTYPE << ", \"mncontrol\":{ \"id\":\"" << id.c_str() << "\"," "\"status\":\"" << status << "\"} }"; - std::string string_reply = reply_oss.str(); - strncpy( (char *)tx_array, string_reply.c_str(), (sizeof(tx_array)-1)); - printf("%s:%d: Sending Reply to APP: %s\r\n",__func__,__LINE__, tx_array); - BLE.SendFile(tx_array,strlen((char *)tx_array)); - break; - } - case SETPOINT_CONTROL_MTYPE: - case DESTROY_SETPOINT_MTYPE: { - - printf("%s:%d: Invoke Setpoint Control COMMAND VIA BLE\r\n", __func__,__LINE__); - std::string json_string = BLE_ReceiveString; - bool status = !StoreReceivedFile( json_string ); - - std::ostringstream reply_oss; - cJSON *spcontrol = cJSON_GetObjectItem(root, "spcontrol"); - std::string id = cJSON_GetObjectItem(spcontrol, "id")->valuestring; - reply_oss << "{ \"mtype\":" << SETPOINT_CONTROL_REPLY_MTYPE << ", \"spcontrol\":{ \"id\":\"" << id.c_str() << "\"," "\"status\":\"" << status << "\"} }"; - std::string string_reply = reply_oss.str(); - strncpy( (char *)tx_array, string_reply.c_str(), (sizeof(tx_array)-1)); - printf("%s:%d: Sending Reply to APP: %s\r\n",__func__,__LINE__, tx_array); - BLE.SendFile(tx_array,strlen((char *)tx_array)); - break; - } - case INPUT_CONFIG_MTYPE: { - - printf("%s:%d: Invoke Configure Input COMMAND VIA BLE\r\n", __func__,__LINE__); - std::string json_string = BLE_ReceiveString; - bool status = !StoreReceivedFile( json_string ); - printf("%s:%d: Store file finished\r\n",__func__,__LINE__); - std::ostringstream reply_oss; - cJSON *input = cJSON_GetObjectItem(root, "input"); - std::string id = cJSON_GetObjectItem(input, "id")->valuestring; - reply_oss << "{ \"mtype\":" << INPUT_CONFIG_REPLY_MTYPE << ", \"input\":{ \"id\":\"" << id.c_str() << "\"," "\"status\":\"" << status << "\"} }"; - std::string string_reply = reply_oss.str(); - strncpy( (char *)tx_array, string_reply.c_str(), (sizeof(tx_array)-1)); - printf("%s:%d: Sending Reply to APP: %s\r\n",__func__,__LINE__, tx_array); - BLE.SendFile(tx_array,strlen((char *)tx_array)); - break; - } - case OUTPUT_CONFIG_MTYPE: { - - printf("%s:%d: Invoke Configure Output COMMAND VIA BLE\r\n", __func__,__LINE__); - std::string json_string = BLE_ReceiveString; - bool status = !StoreReceivedFile( json_string ); - - std::ostringstream reply_oss; - cJSON *output = cJSON_GetObjectItem(root, "output"); - std::string id = cJSON_GetObjectItem(output, "id")->valuestring; - reply_oss << "{ \"mtype\":" << OUTPUT_CONFIG_REPLY_MTYPE << ", \"output\":{ \"id\":\"" << id.c_str() << "\"," "\"status\":\"" << status << "\"} }"; - std::string string_reply = reply_oss.str(); - strncpy( (char *)tx_array, string_reply.c_str(), (sizeof(tx_array)-1)); - printf("%s:%d: Sending Reply to APP: %s\r\n",__func__,__LINE__, tx_array); - BLE.SendFile(tx_array,strlen((char *)tx_array)); - break; - } - default: - printf("%s:%d: unknown mtype received: %d\r\n", __func__,__LINE__,mType); - break; - } - BLE_StringReceived = false; - cJSON_Delete(root); - } -#endif - // This will wait 100ms for a message - osEvent evt = BLEHandlerMailBox.get(100); - if (evt.status == osEventMail) { - BLEHandlerReq_t *mail = (BLEHandlerReq_t*)evt.value.p; - printf("%s:%d: Sending Reply to APP: %s\r\n",__func__,__LINE__,mail->reply); - BLE.SendFile((uint8_t *)mail->reply,strlen((char *)mail->reply)); - BLEHandlerMailBox.free(mail); - } - } -} \ No newline at end of file
--- a/ICE-Application/src/BLEDataHandler/BLEDataHandler.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -/****************************************************************************** - * - * File: BLEDDataHandler.h - * Desciption: interface for the ICE BLE Data Handler - * - *****************************************************************************/ -#ifndef BLEDATAHANDLER_H -#define BLEDATAHANDLER_H - -void BLEDataHandler(void const *args); -void BLE_ReplyToHandler( std::string &id, int status, float value ); - -#endif
--- a/ICE-Application/src/CloudDataHandler/CloudDataHandler.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/****************************************************************************** - * - * File: CloudDataHandler.cpp - * Description: - * - *****************************************************************************/ -#include "CloudDataHandler.h" -#include "CloudFileReceiver.h" -#include "LogHandler.h" -#include <stdio.h> -#include <vector> -#include "rtos.h" -#include "global.h" - -// -// The Cloud Data Handler task -// -void CloudDataHandler(void const *args) -{ -#if 0 - int32_t ret; - bool joined; - bool sent; -#endif - - printf("\r%s has started...\n", __func__); - - while ( true ) { - - std::string tmp_buffer; -#if 0 - if (!GLOBAL_mdot->getNetworkJoinStatus()) { - logInfo("network not joined, joining network"); - if ((ret = GLOBAL_mdot->joinNetwork()) != mDot::MDOT_OK) { - printf("failed to join network %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); - joined = false; - } - } else { - joined = true; - } - - sent = LogHandler( joined ); - if( sent == true ) { - // sent a packet, try to receive back. - printf("Sent to gateway: %s", tmp_buffer.c_str()); - std::vector<uint8_t> rcvData(256); - rcvData.clear(); - if ((ret = GLOBAL_mdot->recv(rcvData)) == mDot::MDOT_OK) { - if (!rcvData.empty()) { - std::string rcv_string(rcvData.begin(), rcvData.end()); - printf("Received Data: %s", rcv_string.c_str()); - CloudFileReceiver( &rcv_string, GLOBAL_mdot ); - } - } - } -#endif - Thread::wait(1000); - } -} \ No newline at end of file
--- a/ICE-Application/src/CloudDataHandler/CloudDataHandler.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -#ifndef CLOUDDATAHANDLER_H -#define CLOUDDATAHANDLER_H - -#include <string> - -#define HEARTBEAT_MSG_MTYPE 20 // heartbeat (out) - -#define SETPOINT_CONTROL_MTYPE 100 // create a setpoint control (in) -#define TIMER_CONTROL_MTYPE 101 // create a timer control (in) -#define PID_CONTROL_MTYPE 102 // create a PID control (in) -#define MANUAL_CONTROL_MTYPE 103 // create a manual control (in) -#define COMPOSITE_CONTROL_MTYPE 104 // create a composite control (in) -#define FAILSAFE_CONTROL_MTYPE 105 // create a failsafe control (in) -#define SENSOR_ERR_CONTROL_MTYPE 106 // create a sensor error control (in) -#define SEQUENCE_CONTROL_MTYPE 107 // create a sequence control (in) - -#define INPUT_CONFIG_MTYPE 200 // create an nput (in) -#define OUTPUT_CONFIG_MTYPE 201 // create an output (in) -#define VINPUT_CONFIG_MTYPE 203 // create a virtual input (in) -#define VOUTPUT_CONFIG_MTYPE 204 // create a virtual output (in) -#define HOLDING_CONFIG_MTYPE 205 // create a holding config (in) - -#define VIRTUAL_COMMAND_MTYPE 250 // virtual command (in) -#define EQUATION_COMMAND_MTYPE 251 // equation command (in) - -#define DESTROY_SETPOINT_MTYPE 300 // destroys a setpoint control (in) -#define DESTROY_TIMER_MTYPE 301 // destroys a timer control (in) -#define DESTROY_PID_MTYPE 302 // destroys a PID control (in) -#define DESTROY_MANUAL_MTYPE 303 // destroys a manual control (in) -#define DESTROY_COMPOSITE_MTYPE 304 // destroys a composite control (in) -#define DESTROY_FAILSAFE_MTYPE 305 // destroys a failsafe control (in) -#define DESTROY_SENSOR_ERR_MTYPE 306 // destroys a sensor error control (in) -#define DESTROY_SEQUENCE_MTYPE 307 // destroys a sequence control (in) - - -#define EVENT_LOG_MTYPE 300 // event log, pump actuation, etc. (out) -#define DEVICE_CONN_MTYPE 301 // device connected (out) -#define LIVE_DATA_MTYPE 400 // live data (out) - -// BLE requests -#define BT_MODBUS_HOLD_COMMAND_MTYPE 1000 -#define BT_MODBUS_RAW_COMMAND_MTYPE 1002 -#define BT_MODBUS_COMMAND_REPLY_MTYPE 1001 - -#define BT_GETLOG_COMMAND_MTYPE 1100 -#define BT_GETLOG_COMMAND_REPLY_MTYPE 1101 -#define BT_GETLIVE_COMMAND_MTYPE 1200 - -#define BT_START_CAL_COMMAND_MTYPE 1300 -#define BT_1PT_CAL_COMMAND_MTYPE 1301 - -#define SETPOINT_CONTROL_REPLY_MTYPE 500 -#define TIMER_CONTROL_REPLY_MTYPE 501 -#define MANUAL_CONTROL_REPLY_MTYPE 503 -#define COMPOSITE_CONTROL_REPLY_MTYPE 504 - -#define INPUT_CONFIG_REPLY_MTYPE 550 -#define OUTPUT_CONFIG_REPLY_MTYPE 551 - -#define OPERATION_SUCCESS 0 -#define ORDER_NOT_SUPPORTED 500 -#define DATA_ARRAY_MISSING 501 -#define UNKNOWN_OPERATION 502 - -void CloudDataHandler(void const *args); -bool StoreReceivedFile( std::string &payload_string ); - -#endif \ No newline at end of file
--- a/ICE-Application/src/CloudDataHandler/CloudFileReceiver.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,356 +0,0 @@ -#include "CloudFileReceiver.h" -#include "CloudDataHandler.h" -#include "mbed.h" -#include "global.h" -#include "cJSON.h" -#include <algorithm> -#include <string> -#include "stdio.h" - -char CloudFileReceiverWriteBuf[MAX_FILE_SIZE]; -char CloudFileReceiverReadBuf[MAX_FILE_SIZE]; - -bool StoreReceivedFile( std::string &payload_string ) -{ - FILENAME_STRING filename; - - memset( filename, '\0', sizeof(FILENAME_STRING) ); - - cJSON * root = cJSON_Parse(payload_string.c_str()); - int mType = cJSON_GetObjectItem(root,"mtype")->valueint; -// printf("%s:%d: mtype=%d\r\n", __func__,__LINE__,mType); - switch( mType ) { - case SETPOINT_CONTROL_MTYPE: - case MANUAL_CONTROL_MTYPE: - case TIMER_CONTROL_MTYPE: { - - std::string control_json; - ConfigMessage_t *mail = ConfigHandlerMailBox.alloc(); - memset(mail, 0, sizeof(ConfigMessage_t)); - mail->action = ACTION_CREATE; - - switch( mType ) { - case SETPOINT_CONTROL_MTYPE: { - cJSON * spcontrol = cJSON_GetObjectItem(root,"spcontrol"); - snprintf( filename, sizeof(FILENAME_STRING), "control_sp_%s.json", cJSON_GetObjectItem(spcontrol,"id")->valuestring ); - control_json = cJSON_PrintUnformatted(spcontrol); - mail->control = CONTROL_SETPOINT; - break; - } - case MANUAL_CONTROL_MTYPE: { - cJSON * mncontrol = cJSON_GetObjectItem(root,"mncontrol"); - snprintf( filename, sizeof(FILENAME_STRING), "control_mn_%s.json", cJSON_GetObjectItem(mncontrol,"id")->valuestring ); - control_json = cJSON_PrintUnformatted(mncontrol); - mail->control = CONTROL_MANUAL; - break; - } - case TIMER_CONTROL_MTYPE: { - cJSON * tmcontrol = cJSON_GetObjectItem(root,"tmcontrol"); - snprintf( filename, sizeof(FILENAME_STRING), "control_tm_%s.json", cJSON_GetObjectItem(tmcontrol,"id")->valuestring ); - std::string control_str = cJSON_PrintUnformatted(tmcontrol); - mail->control = CONTROL_TIMER; - break; - } - default: { - printf("%s:%d: Unknown message type: %d\r\n",__func__,__LINE__,mType); - cJSON_Delete(root); - return false; - } - } - - bool status = GLOBAL_mdot->saveUserFile(filename, (void *)control_json.c_str(), MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d\r\n", __LINE__, status); - break; - } - - strncpy(mail->controlFile, filename, sizeof(mail->controlFile)-1); - ConfigHandlerMailBox.put(mail); - - printf("%s:%d: Control JSON: %s\r\n", __func__,__LINE__, control_json.c_str() ); - printf("%s:%d: Sending a create request for control %s type = %u\r\n", __func__,__LINE__, mail->controlFile, mail->control); - break; - } - case INPUT_CONFIG_MTYPE: - case VINPUT_CONFIG_MTYPE: { - - cJSON * input = cJSON_GetObjectItem(root,"input"); - if( mType == INPUT_CONFIG_MTYPE ) { - snprintf( filename, sizeof(FILENAME_STRING), "input_%s%s", cJSON_GetObjectItem(input,"id")->valuestring, ".json" ); - } else { - snprintf( filename, sizeof(FILENAME_STRING), "vinput_%s%s", cJSON_GetObjectItem(input,"id")->valuestring, ".json" ); - } - std::string input_str = cJSON_PrintUnformatted(input); - - - bool status = GLOBAL_mdot->saveUserFile(filename, (void *)input_str.c_str(), MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d", __LINE__, status); - break; - } - - ModbusMasterReq_t *mail = ModbusMasterMailBox.alloc(); - mail->action = ACTION_READ_FILE; - strncpy( mail->msg, filename, (sizeof(mail->msg)-1)); - ModbusMasterMailBox.put(mail); - - printf("%s:%d: INPUT JSON: %s\r\n", __func__,__LINE__, input_str.c_str() ); - printf("%s:%d: Sending New INPUT to ModbusMasterMailBox, filename=%s\r\n", __func__,__LINE__, filename); - break; - } - case OUTPUT_CONFIG_MTYPE: - case VOUTPUT_CONFIG_MTYPE: { - - cJSON * output = cJSON_GetObjectItem(root,"output"); - if( mType == OUTPUT_CONFIG_MTYPE ) { - snprintf( filename, sizeof(FILENAME_STRING), "output_%s%s", cJSON_GetObjectItem(output,"id")->valuestring, ".json" ); - } else { - snprintf( filename, sizeof(FILENAME_STRING), "voutput_%s%s", cJSON_GetObjectItem(output,"id")->valuestring, ".json" ); - } - std::string output_str = cJSON_PrintUnformatted(output); - - bool status = GLOBAL_mdot->saveUserFile(filename, (void *)output_str.c_str(), MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d\r\n", __LINE__, status); - break; - } - - // send a message to the modbus master - ModbusMasterReq_t *mail = ModbusMasterMailBox.alloc(); - mail->action = ACTION_READ_FILE; - strncpy( mail->msg, filename, (sizeof(mail->msg)-1)); - ModbusMasterMailBox.put(mail); - - // send a message to the output master - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - output_mail->action = ACTION_NEW; - strncpy(output_mail->controlFile, filename, sizeof(output_mail->controlFile)-1); - OutputMasterMailBox.put(output_mail); - - printf("%s:%d: OUTPUT JSON: %s\r\n", __func__,__LINE__, output_str.c_str() ); - printf("%s:%d: Sending New OUTPUT to ModbusMaster and OutputMaster, filename=%s\r\n", __func__,__LINE__, filename); - - break; - } - case HOLDING_CONFIG_MTYPE: { - - cJSON * holding = cJSON_GetObjectItem(root,"holding"); - snprintf( filename, sizeof(FILENAME_STRING), "hold_%s%s", cJSON_GetObjectItem(holding,"id")->valuestring, ".json" ); - std::string holding_str = cJSON_PrintUnformatted(holding); - - bool status = GLOBAL_mdot->saveUserFile(filename, (void *)holding_str.c_str(), MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d", __LINE__, status); - break; - } - - ModbusMasterReq_t *mail = ModbusMasterMailBox.alloc(); - mail->action = ACTION_READ_FILE; - strncpy( mail->msg, filename, (sizeof(mail->msg)-1)); - ModbusMasterMailBox.put(mail); - - printf("%s:%d: HOLDING JSON: %s\r\n", __func__,__LINE__, holding_str.c_str() ); - printf("%s:%d: Sending New HOLDING REGISTER to ModbusMasterMailBox, filename=%s\r\n", __func__,__LINE__, filename); - break; - } - case DESTROY_SETPOINT_MTYPE: { - - cJSON * spcontrol = cJSON_GetObjectItem(root,"spcontrol"); - snprintf( filename, sizeof(FILENAME_STRING), "control_sp_%s.json", cJSON_GetObjectItem(spcontrol,"id")->valuestring ); - - // send a message to the configuration handler to create the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_DESTROY; - msg->control = CONTROL_SETPOINT; - strncpy(msg->controlFile, filename, sizeof(msg->controlFile)-1); - - printf("%s:%d: Sending a destroy request for setpoint control %s type = %u\r\n", __func__,__LINE__, msg->controlFile, msg->control); - - ConfigHandlerMailBox.put(msg); - break; - } - case DESTROY_MANUAL_MTYPE: { - - cJSON * mncontrol = cJSON_GetObjectItem(root,"mncontrol"); - snprintf( filename, sizeof(FILENAME_STRING), "control_mn_%s.json", cJSON_GetObjectItem(mncontrol,"id")->valuestring ); - - // send a message to the configuration handler to create the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_DESTROY; - msg->control = CONTROL_MANUAL; - strncpy(msg->controlFile, filename, sizeof(msg->controlFile)-1); - - printf("%s:%d: Sending a destroy request for manual control %s type = %u\r\n", __func__,__LINE__, msg->controlFile, msg->control); - - ConfigHandlerMailBox.put(msg); - break; - } - case DESTROY_TIMER_MTYPE: { - - cJSON * tmcontrol = cJSON_GetObjectItem(root,"tmcontrol"); - snprintf( filename, sizeof(FILENAME_STRING), "control_tm_%s.json", cJSON_GetObjectItem(tmcontrol,"id")->valuestring ); - - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_DESTROY; - msg->control = CONTROL_TIMER; - strncpy(msg->controlFile, filename, sizeof(msg->controlFile)-1); - - printf("%s:%d: Sending a destroy request for timer control %s type = %u\r\n", __func__,__LINE__, msg->controlFile, msg->control); - - ConfigHandlerMailBox.put(msg); - break; - } - case VIRTUAL_COMMAND_MTYPE: { - - cJSON * command = cJSON_GetObjectItem(root,"command"); - snprintf( filename, sizeof(FILENAME_STRING), "cmd_%s.json", cJSON_GetObjectItem(command,"id")->valuestring ); - std::string command_json = cJSON_PrintUnformatted(command); - - bool status = GLOBAL_mdot->saveUserFile(filename, (void *)command_json.c_str(), MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d\r\n", __LINE__, status); - break; - } - - ModbusMasterReq_t *mail = ModbusMasterMailBox.alloc(); - mail->action = ACTION_READ_FILE; - strncpy( mail->msg, filename, (sizeof(mail->msg)-1)); - ModbusMasterMailBox.put(mail); - - printf("%s:%d: COMMAND: %s\r\n", __func__,__LINE__, command_json.c_str() ); - printf("%s:%d: Sending a create command requst to ModbusMaster %s type = %u\r\n", __func__,__LINE__, mail->msg, mail->control); - - break; - } - case BT_MODBUS_HOLD_COMMAND_MTYPE: { - - cJSON * mbcommand = cJSON_GetObjectItem(root,"mbcommand"); - std::string mbcommand_json = cJSON_PrintUnformatted(mbcommand); - ModbusMasterReq_t *mail = ModbusMasterMailBox.alloc(); - mail->action = ACTION_EXEC_CMD; - mail->replyThread = CLOUD_DATA_HANDLER; - strncpy( mail->msg, mbcommand_json.c_str(), (sizeof(mail->msg)-1)); - ModbusMasterMailBox.put(mail); - - printf("%s:%d: MODBUS Command: %s\r\n", __func__,__LINE__, mbcommand_json.c_str()); - - break; - } - default: -// printf("%s:%d: DEFAULT\r\n", __func__,__LINE__); - break; - } - cJSON_Delete(root); - return true; -} - -bool CloudDataHandler_RcvFile = false; -bool CloudFileReceiver( std::string *recv_string ) -{ - bool status; - CloudDataHandler_RcvFile = false; - int sequence; - std::string seq_str = recv_string->c_str(); - std::string remove_seq = "{\"seq\":"; - std::string::size_type i = seq_str.find(remove_seq); - if (i != std::string::npos) { - seq_str.erase(i, remove_seq.length()); - std::string remove_comma = ","; - i = seq_str.find(remove_comma); - if (i != std::string::npos) { - seq_str.erase(i, (seq_str.length()-i)); - sequence = atoi(seq_str.c_str()); - } - } - std::string payload_string = recv_string->c_str(); - std::string extract_pay = "\"pay\":"; - i = payload_string.find(extract_pay); - if (i != std::string::npos) { - i = i + extract_pay.length(); - payload_string.erase(0, i); - payload_string.erase((payload_string.length()-1), 1); - if( sequence != -1 ) { - payload_string = payload_string.substr(1, payload_string.size() - 2); - } - } - - printf("sequence=%d, payload_string:%s", sequence, payload_string.c_str() ); - - CloudDataHandler_RcvFile = false; - if( sequence == -1 ) { - - // sequence of -1 means string sent in 1 chunk. -// printf("%s:%d: payload=%s\r\n",__func__,__LINE__, payload_string.c_str() ); - status = StoreReceivedFile( payload_string ); - - } else if( sequence == 0 ) { - - memset(CloudFileReceiverWriteBuf,0,sizeof(CloudFileReceiverWriteBuf)); - snprintf(CloudFileReceiverWriteBuf, sizeof(CloudFileReceiverWriteBuf), "%s", payload_string.c_str() ); - - printf("(%d)Writing String Length=%d, %s", __LINE__, MAX_FILE_SIZE, CloudFileReceiverWriteBuf ); - - status = GLOBAL_mdot->saveUserFile("scratch.json", (void *)CloudFileReceiverWriteBuf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d", __LINE__, status); - } else { - CloudDataHandler_RcvFile = true; - printf("(%d)UPDATED scratch.json FILE, status:%d, strlen=%d", __LINE__, status, strlen(CloudFileReceiverWriteBuf)); - } - - } else if( sequence == -2 ) { - - printf("(%d)READING BACK scratch.json FILE FOR LAST PACKET", __LINE__); - - // read the file back - status = GLOBAL_mdot->readUserFile("scratch.json", (void *)CloudFileReceiverReadBuf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)read file failed, status=%d", __LINE__, status); - return false; - } - - snprintf(CloudFileReceiverWriteBuf, sizeof(CloudFileReceiverWriteBuf), "%s%s", CloudFileReceiverReadBuf, payload_string.c_str() ); - printf("(%d)Final String Length=%d, %s", __LINE__, strlen(CloudFileReceiverWriteBuf), CloudFileReceiverWriteBuf ); - - std::string final_json = CloudFileReceiverWriteBuf; - printf("%s:%d: finished parse: %s\r\n", __func__,__LINE__,final_json.c_str() ); - - status = StoreReceivedFile( final_json ); - if( status != true ) { - printf("(%d)save file failed, status=%d", __LINE__, status); - } - - status = GLOBAL_mdot->deleteUserFile("scratch.json"); - if( status != true ) { - printf("(%d)delete file failed, status=%d", __LINE__, status); - } - - printf("(%d)DELETED scratch.json FILE, status:%d", __LINE__, status ); - - } else { - - printf("(%d)READING BACK scratch.json FILE", __LINE__); - - // read the file back - status = GLOBAL_mdot->readUserFile("scratch.json", (void *)CloudFileReceiverReadBuf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)read file failed, status=%d", __LINE__, status); - return false; - } - - snprintf(CloudFileReceiverWriteBuf, sizeof(CloudFileReceiverWriteBuf), "%s%s", CloudFileReceiverReadBuf, payload_string.c_str() ); - printf("(%d)Writing String Length=%d, %s", __LINE__, strlen(CloudFileReceiverWriteBuf), CloudFileReceiverWriteBuf ); - - status = GLOBAL_mdot->saveUserFile("scratch.json", (void *)CloudFileReceiverWriteBuf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d", __LINE__, status); - } else { - CloudDataHandler_RcvFile = true; - printf("(%d)UPDATED scratch.json FILE, status:%d, strlen=%d", __LINE__, status, strlen(CloudFileReceiverWriteBuf)); - } - } - return true; -} \ No newline at end of file
--- a/ICE-Application/src/CloudDataHandler/CloudFileReceiver.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -#ifndef CLOUDFILERECEIVER_H -#define CLOUDFILERECEIVER_H - -#include <string> - -typedef char TASKNAME_STRING[64]; -typedef char FILENAME_STRING[64]; - -extern bool CloudDataHandler_RcvFile; - -bool CloudFileReceiver( std::string *recv_string ); - -#endif -
--- a/ICE-Application/src/CommandParser/cmd.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2725 +0,0 @@ -/* - * =============================================================== - * Natural Tiny Shell (NT-Shell) Application example. - * Version 0.0.6 - * =============================================================== - * Copyright (c) 2010-2011 Shinichiro Nakamura - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * =============================================================== - */ - -#include <mbed.h> -#include <mbed_stats.h> -#include <stdlib.h> -#include <vector> -#include <rtos.h> -#include "cmd.h" -#include "ntshell.h" -#include "ntopt.h" -#include "cJSON.h" -#include "v7.h" -#include "global.h" -#include "OutputTask.h" -#include "ModbusMasterApi.h" -#include "ConfigurationHandler.h" -#include "mfs.h" -#include "rtc.h" -#include "ConfigFs.h" -#include "version.h" -#include "ICELog.h" -#include "utilities.h" - -ntshell_t ntshell; -extern Serial console; - -// the readers and writers -int func_read(char *buf, int cnt); -int func_write(const char *buf, int cnt); -int func_cb_ntshell(const char *text); -void func_cb_ntopt(int argc, char **argv); - -// the command table element -typedef struct { - char *command; // command (from shell) - char *description; // descrption - void (*func)(int argc, char **argv); // callback function -} command_table_t; - -const command_table_t cmdlist[] = { - {"?", "help command", cmd_help }, - {"cif", "create an input file", cmd_cif }, - {"cof", "create an output file", cmd_cof }, - {"create-ca", "create a control algorithm file", cmd_createCAlg }, - {"create-fs", "create a failsafe control", cmd_createFailsafe }, - {"create-mn", "create a manual control", cmd_createManual }, - {"create-se", "create a sensor error control", cmd_createSError }, - {"create-seq", "create a sequence control", cmd_createSequence }, - {"create-sp", "create a setpoint control", cmd_createSetpoint }, - {"create-tm", "create a timer control", cmd_createTimer }, - {"create-vreg", "create a virtual register", cmd_createVreg }, - {"debug-fs", "debug failsafe controls", cmd_debug_fs }, - {"debug-mn", "debug manual controls", cmd_debug_mn }, - {"debug-se", "debug sensor error controls", cmd_debug_se }, - {"debug-seq", "debug sequence controls", cmd_debug_seq }, - {"debug-sp", "debug setpoint controls", cmd_debug_sp }, - {"debug-tm", "debug timer controls", cmd_debug_tm }, - {"delete-file", "delete a file from EEPROM", cmd_deleteFile }, - {"destroy-control", "destroy a control", cmd_destroy }, - {"format-fs", "format EEPROM file system", cmd_formatFS }, - {"heap-stats", "dump heap data", cmd_heap_stats }, - {"help", "help command", cmd_help }, - {"json-test", "JSON parser test", cmd_json }, - {"log-level", "set debug log-level", cmd_logLevel }, - {"ls", "list files in EEPROM", cmd_lsFile }, - {"modmap", "dump modbus register map", cmd_modmap }, - {"preload", "pre-load phase-1 demo files", cmd_preload }, - {"read-file", "read file from EEPROM", cmd_readFile }, - {"reset", "software reset", cmd_reset }, - {"simall", "simulate multiple inputs", cmd_simall }, - {"simerr", "simulate input error", cmd_simerr }, - {"simin", "simulate input", cmd_simin }, - {"show-ca", "show control algorithms", cmd_ShowAlgorithms }, - {"show-controls", "show active controls", cmd_ShowControls }, - {"show-tm-controls", "display active timer controls", cmd_ShowTimers }, - {"show-mn-controls", "display active manual controls", cmd_ShowManuals }, - {"show-co-controls", "display active composite controls", cmd_ShowComposites }, - {"show-fs-controls", "display active failsafe controls", cmd_ShowFailsafes }, - {"show-se-controls", "display active sensor error controls", cmd_ShowSensorErrors}, - {"show-seq-controls", "display active sequential controls", cmd_ShowSequences }, - {"show-sp-controls", "display active setpoint controls", cmd_ShowSetpoints }, - {"show-outputs", "display the output hierarchy", cmd_outputs }, - //{"spi-test", "test SPI FLASH interface", cmd_spiTest }, - {"stack-stats", "dump stack stats", cmd_stack }, - {"test-log-level", "tests the log functions", cmd_testLog }, - {"test-prebleed", "test the pre-bleed scripts", cmd_testPreBleed }, - {"v7-test", "V7 test", cmd_v7, }, - {"version", "display version info", cmd_version }, - {"vregmap", "display the virtual register map", cmd_vregmap }, - {"write-file", "write a file to EEPROM", cmd_writeFile }, - {"get-time", "get time from RTC", cmd_getTime }, - {"set-time", "set time from RTC", cmd_setTime }, - {NULL, NULL, NULL} -}; - - -void cmd_testLog(int argc, char **argv) -{ - logFatal ("Testing"); - logError ("Testing"); - logWarning ("Testing"); - logInfo ("Testing"); - logDebug ("Testing"); - logTrace ("Testing"); - -} - -// -// function: cmd_debug_fs -// decription: debug failsafe controls (ON|OFF) -// -void cmd_debug_fs(int argc, char **argv) -{ - extern bool debugFailsafeControl; - - if ( argc == 1 ) { - printf("\rCurrent level is %d\r\n", debugFailsafeControl); - return; - } - - if ( argc != 2 ) { -usage: - printf("\rusage: debug-fs <1|0>\n"); - printf("\rexample: debug-fs 1\n"); - return; - } - - if ( (atoi(argv[1])) == 1 ) { - debugFailsafeControl = true; - } else if ( (atoi(argv[1])) == 0 ) { - debugFailsafeControl = false; - } else { - goto usage; - } -} - -// -// function: cmd__debug_man -// description: debug manual controls (ON|OFF) -// -void cmd_debug_mn(int argc, char **argv) -{ - extern bool debugManualControl; - - if ( argc == 1 ) { - printf("\rCurrent level is %d\r\n", debugManualControl); - return; - } - - if ( argc != 2 ) { -usage: - printf("\rusage: debug-man <1|0>\n"); - printf("\rexample: debug-man 1\n"); - return; - } - - if ( (atoi(argv[1])) == 1 ) { - debugManualControl = true; - } else if ( (atoi(argv[1])) == 0 ) { - debugManualControl = false; - } else { - goto usage; - } -} - -// -// function: cmd_debug_se -// description: debug sensor error controls (ON|OFF) -// -void cmd_debug_se(int argc, char **argv) -{ - extern bool debugSensorError; - - if ( argc == 1 ) { - printf("\rCurrent level is %d\r\n", debugSensorError); - return; - } - - if ( argc != 2 ) { -usage: - printf("\rusage: debug-se <1|0>\n"); - printf("\rexample: debug-se 1\n"); - return; - } - - if ( (atoi(argv[1])) == 1 ) { - debugSensorError = true; - } else if ( (atoi(argv[1])) == 0 ) { - debugSensorError = false; - } else { - goto usage; - } -} - -// -// function: cmd_debug_seq -// description: debug sequence controls (ON|OFF -// -void cmd_debug_seq(int argc, char **argv) -{ - extern bool debugSequenceControl; - - if ( argc == 1 ) { - printf("\rCurrent level is %d\r\n", debugSequenceControl); - return; - } - - if ( argc != 2 ) { -usage: - printf("\rusage: debug-seq <1|0>\n"); - printf("\rexample: debug-seq 1\n"); - return; - } - - if ( (atoi(argv[1])) == 1 ) { - debugSequenceControl = true; - } else if ( (atoi(argv[1])) == 0 ) { - debugSequenceControl = false; - } else { - goto usage; - } -} - -// -// function: cmd_debug_sp -// description: debug setpoint controls -// -void cmd_debug_sp(int argc, char **argv) -{ - extern bool debugSetpointControl; - - if ( argc == 1 ) { - printf("\rCurrent level is %d\r\n", debugSetpointControl); - return; - } - - if ( argc != 2 ) { -usage: - printf("\rusage: debug-sp <1|0>\n"); - printf("\rexample: debug-sp 1\n"); - return; - } - - if ( (atoi(argv[1])) == 1 ) { - debugSetpointControl = true; - } else if ( (atoi(argv[1])) == 0 ) { - debugSetpointControl = false; - } else { - goto usage; - } -} - -// -// function: cmd_debug_tm -// description: debug timer controls (ON|OFF) -// -void cmd_debug_tm(int argc, char **argv) -{ - extern bool debugTimerControl; - - if ( argc == 1 ) { - printf("\rCurrent level is %d\r\n", debugTimerControl); - return; - } - - if ( argc != 2 ) { -usage: - printf("\rusage: debug-tm <1|0>\n"); - printf("\rexample: debug-tm 1\n"); - return; - } - - if ( (atoi(argv[1])) == 1 ) { - debugTimerControl = true; - } else if ( (atoi(argv[1])) == 0 ) { - debugTimerControl = false; - } else { - goto usage; - } -} - - -void cmd_heap_stats(int argc, char **argv) -{ -#ifndef __ICCARM__ - UNUSED(argc), UNUSED(argv); - printf("\r%s\r\n", Util_getHeapData().c_str()); -#if 0 - UNUSED(argc); - UNUSED(argv); - - mbed_stats_heap_t s; - mbed_stats_heap_get(&s); - - printf("\rHeap statistics:\n"); - printf("\r bytes allocated......%u\n", s.current_size/1024); - printf("\r max bytes allocated..%u\n", s.max_size/1024); - printf("\r cumulative sum.......%u\n", s.total_size); - printf("\r reserved size........%u\n", s.reserved_size); - printf("\r allocations..........%u\n", s.alloc_cnt); - printf("\r failed allocations...%u\n", s.alloc_fail_cnt); - - return; -#endif -#endif -} - -void cmd_ShowAlgorithms(int argc, char **argv) -{ - UNUSED(argc); - UNUSED(argv); - ConfigurationHandler_showAlgorithms(); - printf("\r\n"); -} - -void cmd_ShowControls(int argc, char **argv) -{ - UNUSED(argc); - UNUSED(argv); - ConfigurationHandler_showControls(); - printf("\r\n"); - -} - -void cmd_reset(int argc, char **argv) -{ - NVIC_SystemReset(); -} - -void cmd_ShowTimers(int argc, char **argv) -{ - UNUSED(argc), UNUSED(argv); - ConfigurationHandler_showTimerControls(); - printf("\r\n"); -} - -void cmd_ShowManuals(int argc, char **argv) -{ - UNUSED(argc), UNUSED(argv); - ConfigurationHandler_showManualControls(); - printf("\r\n"); -} - -void cmd_ShowFailsafes(int argc, char **argv) -{ - UNUSED(argc), UNUSED(argv); - ConfigurationHandler_showFailsafeControls(); - printf("\r\n"); -} - -void cmd_ShowSequences(int argc, char **argv) -{ - UNUSED(argc), UNUSED(argv); - ConfigurationHandler_showSequenceControls(); -} - -void cmd_ShowComposites(int argc, char **argv) -{ - UNUSED(argc), UNUSED(argv); - ConfigurationHandler_showCompositeControls(); - printf("\r\n"); -} - -void cmd_ShowSetpoints(int argc, char **argv) -{ - UNUSED(argc), UNUSED(argv); - ConfigurationHandler_showSetpointControls(); - printf("\r\n"); -} - -void cmd_ShowSensorErrors(int argc, char **argv) -{ - UNUSED(argc), UNUSED(argv); - ConfigurationHandler_showSensorErrorControls(); - printf("\r\n"); -} - - -/***************************************************************************** - * Function: cmd_simerr - * Description: simulate an input error - * - * @return none - *****************************************************************************/ -void cmd_simerr(int argc, char **argv) -{ - if ( argc < 3 ) { - printf("\rusage: simerr <input> <errflag_value>\n"); - printf("\rexample: simerr i_bdcond01 9\n"); - return; - } - - ModbusRegisterMap[argv[1]].simulated = true; - SimulateInputMap[argv[1]].errflag = (uint32_t)(atoi(argv[2])); -} - -/***************************************************************************** - * Function: cmd_simin - * Description: simulat input - * - * @param argc-> number of args - * @param argv-> input - * @return none - *****************************************************************************/ -void cmd_simin(int argc, char **argv) -{ - if ( argc != 3 && argc != 7 ) { - printf("\rusage: simin <input> <value> <<low> <hi> <up_step> <down_step>>\r\n"); - printf("\rexample: simin i_tra01 100 94 106 1 .25\r\n"); - printf("\rexample: simin i_bdcond01 2000 1990 2006 .25 1\r\n"); - printf("\rexample: simin i_bdcond01 1800\n"); - return; - } - - //float value = atof( argv[2] ); - SimulateInputMap[argv[1]].start_value = atof(argv[2]); - - ModbusRegisterMap[argv[1]].simulated = true; - - if ( argc == 3 ) { - SimulateInputMap[argv[1]].min = 0; - SimulateInputMap[argv[1]].max = 0; - SimulateInputMap[argv[1]].moving_up = false; - return; - } - SimulateInputMap[argv[1]].min = atof(argv[3]); - SimulateInputMap[argv[1]].max = atof(argv[4]); - SimulateInputMap[argv[1]].up_step = atof(argv[5]); - SimulateInputMap[argv[1]].down_step = atof(argv[6]); - SimulateInputMap[argv[1]].moving_up = true; - -} - -/***************************************************************************** - * Function: cmd_simall - * Description: simulat multiple inputs - * - * @return none - *****************************************************************************/ -void cmd_simall(int argc, char **argv) -{ - - if ( argc > 1 ) { - - printf("\r setting: simin i_tra01 100\n"); - ModbusRegisterMap["i_tra01"].simulated = true; - SimulateInputMap["i_tra01"].start_value = 130; - SimulateInputMap["i_tra01"].min = 0; - SimulateInputMap["i_tra01"].max = 0; - SimulateInputMap["i_tra01"].up_step = 0; - SimulateInputMap["i_tra01"].down_step = 0; - SimulateInputMap["i_tra01"].moving_up = true; - - printf("\r setting: i_tag01 100\n"); - ModbusRegisterMap["i_tag01"].simulated = true; - SimulateInputMap["i_tag01"].start_value = 100; - SimulateInputMap["i_tag01"].min = 0; - SimulateInputMap["i_tag01"].max = 0; - SimulateInputMap["i_tag01"].up_step = 0; - SimulateInputMap["i_tag01"].down_step = 0; - SimulateInputMap["i_tag01"].moving_up = true; - - printf("\r setting: i_bdcond01 1800\n"); - ModbusRegisterMap["i_bdcond01"].simulated = true; - SimulateInputMap["i_bdcond01"].start_value = 1800; - SimulateInputMap["i_bdcond01"].min = 0; - SimulateInputMap["i_bdcond01"].max = 0; - SimulateInputMap["i_bdcond01"].up_step = 0; - SimulateInputMap["i_bdcond01"].down_step = 0; - SimulateInputMap["i_bdcond01"].moving_up = true; - - printf("\r setting: i_cond_temp01 25\n"); - ModbusRegisterMap["i_cond_temp01"].simulated = true; - SimulateInputMap["i_cond_temp01"].start_value = 25000.0; - SimulateInputMap["i_cond_temp01"].min = 0; - SimulateInputMap["i_cond_temp01"].max = 0; - SimulateInputMap["i_cond_temp01"].up_step = 0; - SimulateInputMap["i_cond_temp01"].down_step = 0; - SimulateInputMap["i_cond_temp01"].moving_up = true; - - printf("\r setting: i_ph01 8\n"); - ModbusRegisterMap["i_ph01"].simulated = true; - SimulateInputMap["i_ph01"].start_value = 8000; - SimulateInputMap["i_ph01"].min = 0; - SimulateInputMap["i_ph01"].max = 0; - SimulateInputMap["i_ph01"].up_step = 0; - SimulateInputMap["i_ph01"].down_step = 0; - SimulateInputMap["i_ph01"].moving_up = true; - - printf("\r setting: i_ph_temp01 25\n"); - ModbusRegisterMap["i_ph_temp01"].simulated = true; - SimulateInputMap["i_ph_temp01"].start_value = 25000.0; - SimulateInputMap["i_ph_temp01"].min = 0; - SimulateInputMap["i_ph_temp01"].max = 0; - SimulateInputMap["i_ph_temp01"].up_step = 0; - SimulateInputMap["i_ph_temp01"].down_step = 0; - SimulateInputMap["i_ph_temp01"].moving_up = true; - - printf("\r setting: i_orp01 350\n"); - ModbusRegisterMap["i_orp01"].simulated = true; - SimulateInputMap["i_orp01"].start_value = 350; - SimulateInputMap["i_orp01"].min = 0; - SimulateInputMap["i_orp01"].max = 0; - SimulateInputMap["i_orp01"].up_step = 0; - SimulateInputMap["i_orp01"].down_step = 0; - SimulateInputMap["i_orp01"].moving_up = true; - - - printf("\r setting: i_flowsw01 1\n"); - ModbusRegisterMap["i_flowsw01"].simulated = true; - SimulateInputMap["i_flowsw01"].start_value = 1; - SimulateInputMap["i_flowsw01"].min = 0; - SimulateInputMap["i_flowsw01"].max = 0; - SimulateInputMap["i_flowsw01"].up_step = 0; - SimulateInputMap["i_flowsw01"].down_step = 0; - SimulateInputMap["i_flowsw01"].moving_up = true; - - return; - } - - printf("\rsetting: simin i_tra01 130 120 140 1 .25\r\n"); - printf("\rsetting: simin i_tag01 100 90 110 1 .25\r\n"); - printf("\rsetting: simin i_bdcond01 1800 1725 1875 1 5\r\n"); - printf("\rsetting: simin i_ph01 8.0 7.25 8.75 .0. .1\r\n"); - printf("\rsetting: simin i_orp01 350 300 400 5 1\r\n"); - - ModbusRegisterMap["i_tra01"].simulated = true; - SimulateInputMap["i_tra01"].start_value = 130; - SimulateInputMap["i_tra01"].min = 120; - SimulateInputMap["i_tra01"].max = 140; - SimulateInputMap["i_tra01"].up_step = 1; - SimulateInputMap["i_tra01"].down_step = .25; - - ModbusRegisterMap["i_tag01"].simulated = true; - SimulateInputMap["i_tag01"].start_value = 100; - SimulateInputMap["i_tag01"].min = 90; - SimulateInputMap["i_tag01"].max = 110; - SimulateInputMap["i_tag01"].up_step = 1; - SimulateInputMap["i_tag01"].down_step = .25; - - ModbusRegisterMap["i_bdcond01"].simulated = true; - SimulateInputMap["i_bdcond01"].start_value = 1800; - SimulateInputMap["i_bdcond01"].min = 1725; - SimulateInputMap["i_bdcond01"].max = 1875; - SimulateInputMap["i_bdcond01"].up_step = 1; - SimulateInputMap["i_bdcond01"].down_step = 5; - - ModbusRegisterMap["i_ph01"].simulated = true; - SimulateInputMap["i_ph01"].start_value = 8000; - SimulateInputMap["i_ph01"].min = 7250; - SimulateInputMap["i_ph01"].max = 8750; - SimulateInputMap["i_ph01"].up_step = 5; - SimulateInputMap["i_ph01"].down_step = 10; - - ModbusRegisterMap["i_orp01"].simulated = true; - SimulateInputMap["i_orp01"].start_value = 350; - SimulateInputMap["i_orp01"].min = 300; - SimulateInputMap["i_orp01"].max = 400; - SimulateInputMap["i_orp01"].up_step = 5; - SimulateInputMap["i_orp01"].down_step = 1; - -} - -void cmd_stack(int argc, char **argv) -{ - vector<pair<string, Thread*> > taskList; - - const char *mapper[] = { "Inactive", - "Ready", - "Running", - "WaitingDelay", - "WaitingInterval", - "WaitingOr", - "WaitingAnd", - "WaitingSempahore", - "WaitingMailbox", - "WaitingMutex" - }; - - //simply add your task to the list... - taskList.push_back(make_pair((string)"AnalyticsLogger", GLOBAL_analyticsLogger_thread)); - taskList.push_back(make_pair((string)"ConfigHandler", GLOBAL_configHandler_thread)); - taskList.push_back(make_pair((string)"ControlTask", GLOBAL_controlTask_thread)); - taskList.push_back(make_pair((string)"ModbusMaster", GLOBAL_modbusMaster_thread)); - taskList.push_back(make_pair((string)"OutputTask", GLOBAL_outputTask_thread)); - - for ( vector<pair<string, Thread*> >::iterator pos = taskList.begin(); - pos != taskList.end(); ++ pos) { - printf("\r %-32s size/free/used/max = %5u/%5u/%5u/%5u\tpri=%u state=%-20s\n", - pos->first.c_str(), - pos->second->stack_size(), - pos->second->free_stack(), - pos->second->used_stack(), - pos->second->max_stack(), - pos->second->get_priority(), - mapper[pos->second->get_state()]); - } - - printf("\r\n"); -} - -void cmd_version(int argc, char **argv) -{ - printf("\rversion: %u.%u.%u (%s)\n\r\n", MAJOR_VERSION_NUMBER, - MINOR_VERSION_NUMBER, - PATCH_VERSION_NUMBER, - PROJECT_CODE_NAME); -} - -void cmd_help(int argc, char **argv) -{ - UNUSED(argc); - UNUSED(argv); - - const command_table_t *tblPtr = cmdlist; - - while (tblPtr->command) { - printf("\r%-32s:\t%s\n", tblPtr->command, tblPtr->description); - tblPtr++; - } - printf("\r\n"); -} - -void cmd_v7(int argc, char **argv) -{ - struct v7 *v7 = v7_create(); - v7_val_t exec_rc; - const char *js_code = "print(\"Hello World!\")"; - - v7_exec(v7, js_code, &exec_rc); - printf("\r->exec_rc = %d\r\n", (int)exec_rc); - v7_destroy(v7); -} - -void cmd_json(int argc, char **argv) -{ - const char buf[] = { - "{" - "\"tag1\":\"value1\" ," - "\"tag2\":\"value2\" " - "}" - }; - - cJSON *root = cJSON_Parse(buf); - printf("\rtag1 = %s\n", cJSON_GetObjectItem(root, "tag1")->valuestring); - printf("\rtag2 = %s\n", cJSON_GetObjectItem(root, "tag2")->valuestring); - cJSON_Delete(root); -} - -/***************************************************************************** - * Function: cmd_outputs - * Description: display outputs - ****************************************************************************/ -void cmd_outputs(int argc, char **argv) -{ - UNUSED(argc); - UNUSED(argv); - - DisplayOutputs(); -} - -void cmd_formatFS(int argc, char **argv) -{ - mfs fs0(0xA0); - console.printf("\n\r\n\r[mFS] Formatting EEPROM....................."); - int bad_blocks = fs0.mkfs(true); - console.printf(" found %d bad blocks\r\n", bad_blocks); -} - -void cmd_writeFile(int argc, char **argv) -{ - mfs fs0(0xA0); - - char filename[FILENAME_LENGTH]; - memset( filename, '\0', FILENAME_LENGTH ); - strncpy( filename, argv[1], FILENAME_LENGTH-1 ); - - char *file_buf = (char*) malloc(MAX_FILE_SIZE); - memset( file_buf, '\0', MAX_FILE_SIZE); - snprintf(file_buf, MAX_FILE_SIZE, "1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890"); - printf("%s\r\n", file_buf); - if( GLOBAL_mdot->saveUserFile( filename, (void *)file_buf, MAX_FILE_SIZE ) != true ) { - printf("Failed saveUserFile\r\n"); - } - free(file_buf); - - return; -} - -void cmd_readFile(int argc, char **argv) -{ - char filename[FILENAME_LENGTH]; - memset( filename, '\0', FILENAME_LENGTH ); - strncpy( filename, argv[1], FILENAME_LENGTH-1 ); - int file_size = (atoi(argv[2])); - - char *file_buf = (char*) malloc(MAX_FILE_SIZE*2); - memset( file_buf, '\0', MAX_FILE_SIZE*2); - if( GLOBAL_mdot->readUserFile( filename, (void *)file_buf, file_size ) != true ) { - printf("Failed readUserFile\r\n"); - } - printf("%s\r\n", file_buf); - free(file_buf); - - return; -} - -void cmd_deleteFile(int argc, char **argv) -{ - char filename[FILENAME_LENGTH]; - memset( filename, '\0', FILENAME_LENGTH ); - strncpy( filename, argv[1], FILENAME_LENGTH-1 ); - - if( GLOBAL_mdot->deleteUserFile( filename ) != true ) { - printf("Failed readUserFile\r\n"); - } - - return; -} - -void cmd_lsFile( int argc, char ** argv ) -{ - - std::vector<std::string>::iterator file; - std::vector<std::string> file_list; - file_list = GLOBAL_mdot->listUserFiles(); - for(file = file_list.begin(); file != file_list.end(); ++file) { - printf("%s\r\n", file->c_str() ); - } - - mfs fs0(0xA0); - // Get number of free blocks - char iFreeBlocks = fs0.free(); - // Calculate amount of space used - unsigned int iSpaceUsed = VOL_SIZE - (iFreeBlocks*BS); - // Summary - printf("\n\r%u/%u kB space used, %u blocks free\n\r\n\r", iSpaceUsed/1024, VOL_SIZE/1024, iFreeBlocks); -} - -/** - * Serial read function. - */ -int func_read(char *buf, int cnt) -{ - for (int i = 0; i < cnt; i++) { - buf[i] = console.getc(); - } - - return 0; -} - -/** - * Serial write function. - */ -int func_write(const char *buf, int cnt) -{ - for (int i = 0; i < cnt; i++) { - console.putc(buf[i]); - } - return 0; -} - -/** - * Callback function for ntshell module. - */ -int func_cb_ntshell(const char *text) -{ - return ntopt_parse(text, func_cb_ntopt); -} - -/** - * Callback function for ntopt module. - */ -void func_cb_ntopt(int argc, char **argv) -{ - if (argc == 0) { - return; - } - int execnt = 0; - const command_table_t *p = &cmdlist[0]; - while (p->command != NULL) { - if (strcmp(argv[0], p->command) == 0) { - p->func(argc, argv); - execnt++; - } - p++; - } - if (execnt == 0) { - printf("Command not found.\r\n"); - } - wait_ms(250); -} - -#if 0 -/***************************************************************************** - * Function: cmd_createSequence - * Description: create an administrative control - * - * @param argc-> number of args - * @param argv-> control name, control type - * @return none - *****************************************************************************/ -void cmd_createSequence(int argc, char **argv) -{ - char buf[MAX_FILE_SIZE*2]; - - // let's crreate the sequence control file - std::string filename = "control_seq_SLUG-DOSE.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"SLUG-DOSE\", " - "\"startTrigger\": \"v_slugSchedule\", " - "\"sequence\": " - "[" - // START PRE-BLEED elements - "{ \"startTrigger\":\"v_alwaysTrue\", " - "\"actions\": " - "[" - "{ \"action\": \"create\", \"id\": \"PRE-BLEED-SP\" }, " - "{ \"action\": \"create\", \"id\": \"GUARD-TM\" }" - "]," // end actions array - "\"stopTrigger\": \"v_PreBleedDone\" }," - - // DELETE PRE-BLEED elements - "{ \"startTrigger\":\"v_alwaysTrue\", " - "\"actions\": " - "[" - "{ \"action\": \"delete\", \"id\": \"PRE-BLEED-SP\" }, " - "{ \"action\": \"delete\", \"id\": \"GUARD-TM\" }" - "]," - "\"stopTrigger\": \"v_alwaysTrue\" }," - - // SLUG-FEED elements - "{ \"startTrigger\":\"v_alwaysTrue\", " - "\"actions\": " - "[" - "{ \"action\": \"create\", \"id\": \"SLUG-FEED-SP\" } " - "]," - "\"stopTrigger\": \"v_SlugFeedDone\" }," - - // DELETE SLUG-FEED elements - "{ \"startTrigger\":\"v_alwaysTrue\", " - "\"actions\": " - "[" - "{ \"action\": \"delete\", \"id\": \"SLUG-FEED-SP\" } " - "]," - "\"stopTrigger\": \"v_alwaysTrue\" }" - "] " // end sequence array - "} "); - - printf("String Length=%d\r\n", strlen(buf) ); - bool rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, sizeof(buf)); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - // the pre-bleed setpoint control - snprintf(buf, sizeof(buf), - "{" - "\"id\": \"PRE-BLEED-SP\"," - "\"priority\": \"750\", " - "\"input\": \"i_bdcond\" ," - "\"output\": \"o_rly01\" ," - "\"setpoint\": \"1750\", " - "\"prodfact\": \"\", " - "\"actingDir\": \"1\", " - "\"tol\": \"5\" " - "}"); - filename = "seq_SLUG-DOSE_PRE-BLEED-SP.json"; - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void*)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // OUTPUT: Virtual Output - this is the START TRIGGER fro the control - filename = "vregister_slugSchedule.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"v_slugSchedule\", " - "\"value\": \"0\" " - "}"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // VIRTUAL REGISTER: always True - filename = "vregister_alwaysTrue.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"v_alwaysTrue\", " - "\"value\": \"1\" " - "}"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // VIRTUAL REGISTER: v_GuardTimer - filename = "vregister_GuardTimer.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"v_GuardTimer\", " - "\"value\": \"0\" " - "}"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // VIRTUAL REGISTER: v_PreBleedDone - filename = "vreg_PreBleedDone.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"v_PreBleedDone\", " - "\"value\": \"0\" " - "}"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // VIRTUAL REGISTER: v_SlugFeedDone - filename = "vreg_SlugFeedDone.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"v_SlugFeedDone\", " - "\"value\": \"0\" " - "}"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - - // CONTROL: GUARD-TIMER - filename = "seq_SLUG-DOSE_GUARD-TM.json"; - snprintf(buf, sizeof(buf), - "{" - "\"id\":\"GUARD-TM\"," - "\"output\":\"v_GuardTimer\"," - "\"priority\":\"750\"," - "\"day\":\"\"," - "\"startHour\":\"\"," - "\"startMin\":\"\"," - "\"startSec\":\"\"," - "\"week\":\"\"," - "\"duration\":\"60\" " - "}"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void*)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("failed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // the pre-bleed setpoint control - snprintf(buf, sizeof(buf), - "{" - "\"id\": \"SLUG-FEED-SP\"," - "\"priority\": \"750\", " - "\"input\": \"i_orp01\" ," - "\"output\": \"o_rly02\" ," - "\"setpoint\": \"400\", " - "\"prodfact\": \"\", " - "\"actingDir\": \"1\", " - "\"tol\": \"5\" " - "}"); - filename = "seq_SLUG-DOSE_SLUG-FEED-SP.json"; - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void*)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } -} -#endif - -// PRELOAD command, used to create all the pertinent inputs, outputs, and -// controls needed to achieve a basic cooling water application -void cmd_preload(int argc, char **argv) -{ - UNUSED(argc); - UNUSED(argv); - - char buf[MAX_FILE_SIZE]; - bool rc; - - // PRELOADER: - // JavaScripts: - // js_noFlowDelay.js // implement 10 minute on delay for flow switch - // js_sum.js // sum two registers and store in register - // js_diff.js // diff two registers and store in register - // js_mul.js // mul two registers and store in register - // js_div.js // div two registers and store in register - // js_equ.js // equ set one register equal to another register - // - // Inputs: - // input_i_bdcond.json // conductivity probe - // input_i_bdcond_temp.json // conductivity probe : temperature - // input_i_flowsw01.json // flow switch - // input_i_orp01.json // ORP probe - // input_i_ph01.json // pH probe - // input_i_ph01_temp.json // pH probe : temperature - // input_i_tag01.json // tag (flu) - // input_i_tra01.json // Trasar - // - // Outputs: - // output_o_rly01.json // relay-1 - // output_o_rly02.json // relay-2 - // - // Standard Controls: - // control_sp_i_bd01.json // blowdown - // control_sp_i_tra01.json // Trasar - // - // Control Algorithms: - // control_ca_eq1.json // == 1 - // control_ca_eq129.json // == 129 - // - // Composite Controls: - // control_comp_flow.json // flow override - // control_comp_lockout.json // lockout blowdown - - std::string filename = "ble_device_name.json"; - - snprintf(buf, sizeof(buf), "{\"id\":\"ICE-Ecolab\"}"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - filename = "vreg_const_1000.json"; - - // Constant: 1000.0 - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"v_Const_1000\", " - "\"value\": \"1000.0\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - -#ifdef EXECUTE_SCRIPT - - filename = "vreg_noFlowDelaySig.json"; - - // Virtual Register: v_noFlowDelaySig - // Used to generate on delay timing for flow override. - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"v_noFlowDelaySig\", " - "\"value\": \"1\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - filename = "vreg_noFlowTimeStamp.json"; - - // Virtual Register: v_noFlowTimeStamp - // Used to generate on delay timing for flow override. - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"v_noFlowTimeStamp\", " - "\"value\": \"0\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - filename = "js_noFlowDelay.js"; - - // JavaScript: sum(a, b, c) - // a = Flow Input Signal - this is the signal we are doing the on delay timing for - // b = v_noFlowDelaySig - signal generated after No Flow condition exsits for 10 minutes - // c = v_noFlowTimeStamp - mark of when the no flow signal first started - // - // "\"args\":[{\"arg\":\"i_flowsw01\"},{\"arg\":\"v_noFlowDelaySig\"},{\"arg\":\"v_noFlowTimeStamp\"}]," - // - snprintf(buf, sizeof(buf), - "var noFlowDelay=function(a,b,c){" - "f_state=getRegister(a);" - "if(f_state==1){" - // we have flow on the skid, reset timestamp to 0 - // and reset the "delayed" signal to indicate we have flow - "setRegister(c,0);" - "setRegister(b,f_state);" - "}else{" - // we don't have flow on the skid - "curr_time=getTime();" - "st_tstamp=getRegister(c);" - "if(st_tstamp==0) {" - // this is the 1st read of no-flow - // mark the start of the delay timing - "setRegister(c,curr_time);" - "}else{" - // determine how long we have been doing the delay timing. - "diff_time=curr_time-st_tstamp;" - //"print(' Delay='+diff_time+'\r');" - "if(diff_time>=600){" - // been doing the delay timing for at least 10 minutes - // set the delayed signal to equal the actual flow switch - "setRegister(b,f_state);" - "}" - "}" - "}" - "return 1;" - "};" ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - filename = "js_sum.js"; - - // JavaScript: sum(a, b, c) - snprintf(buf, sizeof(buf), - "var sum = function(a, b, c) {" - "res = (getRegister(a) + getRegister(b));" - "setRegister(c,res);" - "return res;" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - filename = "js_diff.js"; - - // JavaScript: diff(a, b, c) - snprintf(buf, sizeof(buf), - "var diff = function(a, b, c) {" - "res = (getRegister(a) - getRegister(b));" - "setRegister(c,res);" - "return res;" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - filename = "js_div.js"; - - // JavaScript: div(a, b, c) - snprintf(buf, sizeof(buf), - "var div = function(a, b, c) {" - "res = (getRegister(a) / getRegister(b));" - "setRegister(c,res);" - "return res;" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - filename = "js_mul.js"; - - // JavaScript: mul(a, b, c) - snprintf(buf, sizeof(buf), - "var mul = function(a, b, c) {" - "res = (getRegister(a) * getRegister(b));" - "setRegister(c,res);" - "return res;" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - filename = "js_equ.js"; - - // JavaScript: equ(a, b) - snprintf(buf, sizeof(buf), - "var equ = function(a, b) {" - "res = getRegister(a);" - "setRegister(b,res);" - "return res;" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - filename = "exe_js_div_cond_temp01.js"; - - // Execute JavaScript: DIV_COND_TEMP01 - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"DIV_COND_TEMP01\"," - "\"script\":\"div\"," - "\"args\":[{\"arg\":\"i_cond_temp01\"},{\"arg\":\"v_Const_1000\"},{\"arg\":\"i_cond_temp01\"}]" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - filename = "exe_js_div_ph01.js"; - - // Execute JavaScript: DIV_PH01 - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"DIV_PH01\"," - "\"script\":\"div\"," - "\"args\":[{\"arg\":\"i_ph01\"},{\"arg\":\"v_Const_1000\"},{\"arg\":\"i_ph01\"}]" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - filename = "exe_js_prebleed.js"; - // Execute JavaScript: PREBLEED_DONE.json - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"PREBLEED_DONE\"," - "\"script\":\"prebleed_done\"," - // in: o_rly01 - // in: v_pbTimer - // out" v_preBleedDone - "\"args\":[{\"arg\":\"o_rly01\"},{\"arg\":\"v_pbTimer\"},{\"arg\":\"v_preBleedDone\"}]" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - /* - filename = "exe_js_div_ph_temp01.js"; - - // Execute JavaScript: DIV_PH_TEMP01 - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"DIV_PH_TEMP01\"," - "\"script\":\"div\"," - "\"args\":[{\"arg\":\"i_ph_temp01\"},{\"arg\":\"v_Const_1000\"},{\"arg\":\"i_ph_temp01\"}]" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - filename = "exe_js_no_flow_delay.js"; - - // Execute JavaScript: NO_FLOW_DELAY - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"NO_FLOW_DELAY\"," - "\"script\":\"noFlowDelay\"," - "\"args\":[{\"arg\":\"i_flowsw01\"},{\"arg\":\"v_noFlowDelaySig\"},{\"arg\":\"v_noFlowTimeStamp\"}]" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - */ -#endif - - filename = "input_i_bdcond01.json"; - - // INPUT: Conductivity - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"i_bdcond01\"," - "\"name\": \"Tower Conductivity\", " - "\"units\": \"uS\"," - "\"min\": \"0\"," - "\"max\": \"6000\"," - "\"node\": \"21\"," - "\"reg\": \"18\"," - "\"rtype\": \"4\"," - "\"type\": \"1\"," - "\"size\": \"2\"," - "\"order\": \"2\"," - "\"fmt\": \"%%.2f\"," - "\"cmd\": \"\"," - "\"args\": []," - "\"rfreq\": \"5\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // INPUT: Conductivity Temperature - filename = "input_i_cond_temp01.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"i_cond_temp01\"," - "\"name\":\"Temp Conductivity\"," - "\"units\":\"C\"," - "\"min\":\"0\"," - "\"max\":\"80\"," - "\"node\":\"21\"," - "\"reg\":\"10\"," - "\"rtype\":\"4\"," - "\"type\":\"1\"," - "\"size\":\"2\"," - "\"order\":\"2\"," - "\"fmt\":\"%%.2f\"," - "\"cmd\":\"div\"," - "\"args\":[{\"arg\":\"i_cond_temp01\"},{\"arg\":\"v_Const_1000\"},{\"arg\":\"i_cond_temp01\"}]," - "\"rfreq\":\"5\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // INPUT: Flow Switch - filename = "input_i_flowsw01.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"i_flowsw01\"," - "\"name\": \"Flow\"," - "\"units\": \"\"," - "\"min\": \"0\"," - "\"max\": \"1\"," - "\"node\": \"0\"," - "\"reg\": \"1\"," - "\"rtype\": \"0\"," - "\"type\": \"3\"," - "\"size\": \"2\"," - "\"order\": \"0\"," - "\"fmt\": \"%%d\"," - "\"cmd\": \"\"," - "\"args\": []," - "\"rfreq\": \"5\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // INPUT: ORP - filename = "input_i_orp01.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"i_orp01\"," - "\"name\": \"ORP\"," - "\"units\": \"mV\"," - "\"min\": \"0\"," - "\"max\": \"10\"," - "\"node\": \"23\"," - "\"reg\": \"14\"," - "\"rtype\": \"4\"," - "\"type\": \"1\"," - "\"size\": \"2\"," - "\"order\": \"2\"," - "\"fmt\": \"%%.2f\"," - "\"cmd\": \"\"," - "\"args\": []," - "\"rfreq\": \"5\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // INPUT: pH - filename = "input_i_ph01.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"i_ph01\"," - "\"name\":\"pH\"," - "\"units\":\"pH\"," - "\"min\":\"0\"," - "\"max\":\"10\"," - "\"node\":\"22\"," - "\"reg\":\"18\"," - "\"rtype\":\"4\"," - "\"type\":\"1\"," - "\"size\":\"2\"," - "\"order\":\"2\"," - "\"fmt\":\"%%.2f\"," - "\"cmd\":\"div\"," - "\"args\":[{\"arg\":\"i_ph01\"},{\"arg\":\"v_Const_1000\"},{\"arg\":\"i_ph01\"}]," - "\"rfreq\":\"5\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // INPUT: pH Temperature - filename = "input_i_ph01_temp.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"i_ph_temp01\"," - "\"name\":\"Temp pH\"," - "\"units\":\"C\"," - "\"min\":\"0\"," - "\"max\":\"10\"," - "\"node\":\"22\"," - "\"reg\":\"10\"," - "\"rtype\":\"4\"," - "\"type\":\"1\"," - "\"size\":\"2\"," - "\"order\":\"2\"," - "\"fmt\":\"%%.2f\"," - "\"cmd\":\"div\"," - "\"args\":[{\"arg\":\"i_ph_temp01\"},{\"arg\":\"v_Const_1000\"},{\"arg\":\"i_ph_temp01\"}]," - "\"rfreq\":\"5\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // INPUT: Tag - filename = "input_i_tag01.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"i_tag01\"," - "\"name\": \"Tag\", " - "\"units\": \"PPM\", " - "\"min\": \"0\", " - "\"max\": \"300\", " - "\"node\": \"1\", " - "\"reg\": \"11\", " - "\"rtype\": \"4\", " - "\"type\": \"0\", " - "\"size\": \"2\", " - "\"order\": \"2\", " - "\"fmt\": \"%%.2f\", " - "\"cmd\": \"\", " - "\"args\": []," - "\"rfreq\": \"5\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // INPUT: Trasar - filename = "input_i_tra01.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"i_tra01\"," - "\"name\": \"Trasar\", " - "\"units\": \"PPM\", " - "\"min\": \"0\", " - "\"max\": \"300\", " - "\"node\": \"1\", " - "\"reg\": \"9\", " - "\"rtype\": \"4\", " - "\"type\": \"0\", " - "\"size\": \"2\", " - "\"order\": \"2\", " - "\"fmt\": \"%%.2f\", " - "\"cmd\": \"\", " - "\"args\": []," - "\"rfreq\": \"5\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // COMMAND: Divide by 1000 - filename = "cmd_div.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"%s\", " - "\"Operand\":\"%s\", " - "\"Operator\":\"%s\", " - "\"Constant\":\"%s\" } ", "div", "", "/", "1000.0"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // OUTPUT: Relay 01 - filename = "output_o_rly01.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"o_rly01\", " - "\"name\": \"Blowdown\", " - "\"units\": \"\", " - "\"min\": \"0\", " - "\"max\": \"300\", " - "\"node\": \"0\", " - "\"reg\": \"1\", " - "\"rtype\": \"1\", " - "\"type\": \"0\", " - "\"size\": \"2\", " - "\"order\": \"2\", " - "\"fmt\": \"%%.2f\", " - "\"cmd\": \"\", " - "\"args\": [], " - "\"rfreq\": \"5\", " - "\"toperiod\": \"0\", " - "\"scalelo\": \"0\", " - "\"scalehi\": \"100\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // OUTPUT: Relay 02 - filename = "output_o_rly02.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"o_rly02\", " - "\"name\": \"3DTS86\", " - "\"units\": \"\", " - "\"min\": \"0\", " - "\"max\": \"300\", " - "\"node\": \"0\", " - "\"reg\": \"2\", " - "\"rtype\": \"1\", " - "\"type\": \"0\", " - "\"size\": \"2\", " - "\"order\": \"2\", " - "\"fmt\": \"%%.2f\", " - "\"cmd\": \"\", " - "\"args\": [], " - "\"rfreq\": \"5\", " - "\"toperiod\": \"0\", " - "\"scalelo\": \"0\", " - "\"scalehi\": \"100\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // SETPOINT: Blowdown - filename = "control_sp_BLOWDOWN_01.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"BLOWDOWN_01\", " - "\"priority\": \"800\", " - "\"input\": \"i_bdcond01\", " - "\"output\": \"o_rly01\", " - "\"setpoint\": \"1800\", " - "\"prodfact\": \"\", " - "\"actingDir\": \"1\", " - "\"tol\": \"30\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // SETPOINT: Trasar - filename = "control_sp_INH_TRA_01.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"INH_TRA_01\", " - "\"priority\": \"800\", " - "\"input\": \"i_tra01\", " - "\"output\": \"o_rly02\", " - "\"setpoint\": \"130\", " - "\"prodfact\": \"100\", " - "\"actingDir\": \"0\", " - "\"tol\": \"4\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // ALGORITHM == 0 - filename = "control_ca_EQUAL_TO_0.json"; - snprintf(buf, sizeof(buf), - "{" - "\"id\": \"EQUAL_TO_0\", " - "\"opr\": \"0\", " - "\"op\": \"==\", " - "\"true\": \"responseA\", " - "\"false\": \"nothing\" " - "}"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save user file %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // ALGORITHM == 129 - filename = "control_ca_RELAY_ON.json"; - snprintf(buf, sizeof(buf), - "{" - "\"id\": \"RELAY_ON\", " - "\"opr\": \"1\", " - "\"op\": \"&\", " - "\"true\": \"responseA\", " - "\"false\": \"nothing\" " - "}"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save user file %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // FLOW - filename = "control_comp_FLOW.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"FLOW\", " - "\"tag\": \"i_flowsw01\", " - "\"ca\": \"EQUAL_TO_0\", " - "\"priority\": \"300\", " - "\"entries\": \"2\", " - "\"outputs\": [ " - "{" - "\"tag\": \"o_rly01\", " - "\"responseA\": \"fixed off\" " - "}," - "{" - "\"tag\": \"o_rly02\", " - "\"responseA\": \"fixed off\" " - "}" - "]" - "}" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save user file %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // LOCKOUT - filename = "control_comp_LOCKOUT_BD.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"LOCKOUT_BD\", " - "\"tag\": \"o_rly01\", " - "\"ca\": \"RELAY_ON\", " - "\"priority\": \"400\", " - "\"entries\": \"1\", " - "\"outputs\": [ " - "{" - "\"tag\": \"o_rly02\", " - "\"responseA\": \"fixed off\" " - "}" - "]" - "}" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save user file %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - filename = "control_fs_BLOWDOWN_FS.json"; - snprintf(buf, sizeof(buf), - "{" - "\"id\": \"BLOWDOWN_FS\", " - "\"input\": \"i_bdcond01\", " - "\"output\": \"o_rly01\", " - "\"priority\":\"600\", " - "\"lfsValue\":\"700\", " - "\"lfsDutyCycle\":\"0\", " - "\"lfsInterval\":\"1\", " - "\"hfsValue\":\"2700\", " - "\"hfsDutyCycle\":\"50\", " - "\"hfsInterval\":\"1\" " - "}"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to create %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - - Thread::wait(1000); - - filename = "control_fs_TRASAR_FS.json"; - snprintf(buf, sizeof(buf), - " {" - "\"id\": \"TRASAR_FS\", " - "\"input\": \"i_tra01\", " - "\"output\": \"o_rly02\", " - "\"priority\":\"600\", " - "\"lfsValue\":\"70\", " - "\"lfsDutyCycle\":\"50\", " // ON a minute, OFF a minute... - "\"lfsInterval\":\"2\", " - "\"hfsValue\":\"200\", " - "\"hfsDutyCycle\":\"0\", " - "\"hfsInterval\":\"2\" " - "}"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to create %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // HOLDING REGISTER: Conductivity Calibration - filename = "hold_h_CondCmd_21.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"%s\", " - "\"node\":\"%s\", " - "\"sreg\":\"%s\", " - "\"nreg\":\"%s\", " - "\"order\":\"%s\" } ", "h_CondCmd_21", "21", "0", "3", "2"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // HOLDING REGISTER: pH Calibration - filename = "hold_h_phCmd_22.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"%s\", " - "\"node\":\"%s\", " - "\"sreg\":\"%s\", " - "\"nreg\":\"%s\", " - "\"order\":\"%s\" } ", "h_phCmd_22", "22", "0", "3", "2"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - - printf("\r\nIssue \"reset\" command to invoke changes\r\n\r\n\r\n"); - -} - -/***************************************************************************** - * Function: cmd_cif - * Description: create input file - * - * @param argc-> number of args - * @param argv-> filename - * @return none - *****************************************************************************/ -void cmd_cif(int argc, char **argv) -{ - if ( argc != 6 ) { - printf("\rusage: cif <fname> <input> <name> <node> <reg>\n"); - printf("\rexample: cif input_i_tra01.json i_tra01 Trasar 5 2\n"); - return; - } - - char data_buf[MAX_FILE_SIZE]; - snprintf(data_buf, sizeof(data_buf), - "{ " - "\"id\": \"%s\", " - "\"name\": \"%s\", " - "\"units\": \"PPM\", " - "\"min\": \"0\", " - "\"max\": \"300\", " - "\"node\": \"%s\", " - "\"reg\": \"%s\", " - "\"rtype\": \"1\", " - "\"type\": \"0\", " - "\"size\": \"2\", " - "\"order\": \"2\", " - "\"rfreq\": \"5\", " - "\"cmd\": \"\"," - "\"args\": []," - "\"fmt\": \"%%.2f\" } ", argv[2], argv[3], argv[4], argv[5]); - - bool status = GLOBAL_mdot->saveUserFile(argv[1], (void *)data_buf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d", __LINE__, status); - } - - printf("Sending Mail To ModbusMasterMailBox, filename=%s", argv[1]); - ModbusMasterReq_t *mail = ModbusMasterMailBox.alloc(); - mail->action = ACTION_READ_FILE; - strncpy( mail->msg, argv[1], (sizeof(mail->msg)-1)); - ModbusMasterMailBox.put(mail); -} - -/***************************************************************************** - * Function: cmd_destroy - * Description: destroy a control - * - * @param argc-> number of arguments - * @param argv-> control name, control type - * @return none - *****************************************************************************/ -void cmd_destroy(int argc, char **argv) -{ - if ( argc != 3 ) { - printf("\r\nusage: destroy-control [controlName] [controlType]\n"); - printf("\rcontrolType-> 0=timer, 1=PID, 2=setpoint, 3=composite, 4=manual, 5=sequence, 6=failsafe, 7=sensor error\r\n"); - return; - } - - // send a message to the configuration handler to destroy the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_DESTROY; - msg->control = (Control_t) atoi(argv[2]); - strncpy(msg->controlFile, argv[1], sizeof(msg->controlFile)-1); - - printf("%s: Sending a destroy request for control %s\r\n", - __func__, msg->controlFile); - - ConfigHandlerMailBox.put(msg); - printf("\r\n"); - return; -} - -/***************************************************************************** - * Function: cmd_cof - * Description: create output file - * - * @param argc-> number of args - * @param argv-> filename - * @return none - *****************************************************************************/ -void cmd_cof(int argc, char **argv) -{ - // OUTPUT: Relay 01 - char buf[MAX_FILE_SIZE]; - std::string filename = "output_o_rly01.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"o_rly01\", " - "\"name\": \"Blowdown\", " - "\"units\": \"\", " - "\"min\": \"0\", " - "\"max\": \"300\", " - "\"node\": \"0\", " - "\"reg\": \"1\", " - "\"rtype\": \"1\", " - "\"type\": \"0\", " - "\"size\": \"2\", " - "\"order\": \"2\", " - "\"fmt\": \"%%.2f\", " - "\"cmd\": \"\", " - "\"rfreq\": \"5\", " - "\"toperiod\": \"0\", " - "\"scalelo\": \"0\", " - "\"scalehi\": \"100\" }"); - bool rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - Thread::wait(1000); - - // OUTPUT: Relay 02 - filename = "output_o_rly02.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"o_rly02\", " - "\"name\": \"3DTS86\", " - "\"units\": \"\", " - "\"min\": \"0\", " - "\"max\": \"300\", " - "\"node\": \"0\", " - "\"reg\": \"2\", " - "\"rtype\": \"1\", " - "\"type\": \"0\", " - "\"size\": \"2\", " - "\"order\": \"2\", " - "\"fmt\": \"%%.2f\", " - "\"cmd\": \"\", " - "\"rfreq\": \"5\", " - "\"toperiod\": \"0\", " - "\"scalelo\": \"0\", " - "\"scalehi\": \"100\" }"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } -} - -/***************************************************************************** - * Function: cmd_modmap - * Description: dump modbus register map - *****************************************************************************/ -void cmd_modmap(int argc, char **argv) -{ - - UNUSED(argc); - UNUSED(argv); - ModbusValue value; - - std::map<std::string, ModbusRegister>::iterator iter; - for (iter = ModbusRegisterMap.begin(); iter != ModbusRegisterMap.end(); ++iter) { - ModbusMasterReadRegister( iter->first, &value ); - printf("tag=%-24s node=%-4d reg=%-4d rtype=%-2d size=%-2d order=%-2d value=%-12.2f errflag=%-2d argc=%-2d arg[0]=%-24s\r\n", - iter->first.c_str(), iter->second.node, iter->second.reg, - iter->second.rtype, iter->second.size, iter->second.order, - value.value, value.errflag, iter->second.argc, iter->second.argv[0].c_str() ); - } -} - -void cmd_createVreg(int argc, char **argv) -{ - if ( argc != 4 ) { - printf("\rusage: create-vreg <filename> <vregId> <value>\n"); - printf("\rusage: create-vreg vreg_alwaysTrue.json v_alwaysTrue 1\n"); - return; - } - - char buf[MAX_FILE_SIZE]; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"%s\", " - "\"value\": \"%s\" }", argv[2], argv[3]); - bool rc = GLOBAL_mdot->saveUserFile(argv[1], (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", argv[1]); - return; - } else { - printf("\r...generated %s\n", argv[1]); - } -} - -// -void cmd_createCAlg(int argc, char **argv) -{ - if ( argc != 1 ) { - printf("\rusage: create-ca control_ca_equal.json"); - return; - } - - char buf[MAX_FILE_SIZE]; - std::string filename = "control_ca_eq1.json"; - snprintf(buf, sizeof(buf), - " {" - "\"id\": \"EQUAL_TO_1\", " - "\"opr\": \"1\", " - "\"op\": \"==\", " - "\"true\": \"responseA\", " - "\"false\": \"nothing\" " - "}"); - - bool rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save user file %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - -// send a message to the configuration handler to create the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_CREATE; - msg->control = CONTROL_ALGORITHM; - strncpy(msg->controlFile, filename.c_str(), sizeof(msg->controlFile)-1); - - ConfigHandlerMailBox.put(msg); - - Thread::wait(2000); - -// == 129 - filename = "control_ca_eq129.json"; - snprintf(buf, sizeof(buf), - "{" - "\"id\": \"EQUAL_TO_1\", " - "\"opr\": \"1\", " - "\"op\": \"==\", " - "\"true\": \"responseA\", " - "\"false\": \"nothing\" " - "}"); - - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save user file %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // send a message to the configuration handler to create the control - msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_CREATE; - msg->control = CONTROL_ALGORITHM; - strncpy(msg->controlFile, "control_ca_greq1.json", sizeof(msg->controlFile)-1); - - ConfigHandlerMailBox.put(msg); -} - -/***************************************************************************** - * Function: cmd_createManual - * Description: create a manual control - * - * @param argc-> number of args - * @param argv-> filename - * @return none - *****************************************************************************/ -void cmd_createManual(int argc, char **argv) -{ - std::string relayState; - if ( argc != 6 ) { - printf("\rusage: create-mn <filename> <id> <output> <state> <duration>\n"); - printf("\rexample: create-mn control_mn_rly01.json man-1 o_rly1 on 30\r\n"); - printf("\r<duration> specified in seconds, 0 for contiuous\n"); - return; - } - - string state(argv[4]); - - if ( state == "on" ) { - relayState = "1"; - } else if ( state == "off" ) { - relayState = "0"; - } else { - printf("\r<state> must be on or off\r\n"); - return; - } - - unsigned int duration = atoi(argv[5]); - unsigned int type; - if ( duration == 0 ) { - type = MANUAL_CONTROL_TYPE_CONTINUOUS; - } else { - type = MANUAL_CONTROL_TYPE_TIMED; - } - - char data_buf[MAX_FILE_SIZE]; - snprintf(data_buf, sizeof(data_buf), - "{ " - "\"id\": \"%s\", " - "\"output\": \"%s\", " - "\"type\": \"%d\", " - "\"priority\": \"100\", " - "\"duration\": \"%d\", " - "\"setpoint\": \"0\", " - "\"state\": \"%s\", " - "\"percent\": \"100\" }", argv[2], argv[3], type, duration, relayState.c_str() - ); - - bool status = GLOBAL_mdot->saveUserFile(argv[1], (void *)data_buf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d", __LINE__, status); - return; - } - - // send a message to the configuration handler to create the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_CREATE; - msg->control = CONTROL_MANUAL; - strncpy(msg->controlFile, argv[1], sizeof(msg->controlFile)-1); - - printf("%s: Sending a create request for control %s type = %u\r\n", - __func__, msg->controlFile, msg->control); - - ConfigHandlerMailBox.put(msg); - printf("\r\n"); - return; -} - -/***************************************************************************** - * Function: cmd_createSError - * Description: create a sensoe error control - * - * @param argc-> number of args - * @param argv-> filename - * @return none - *****************************************************************************/ -void cmd_createSError(int argc, char **argv) -{ - if ( argc != 7 ) { - printf("\rusage: create-se <filename> <id> <input> <output> <duty> <interval>\n"); - printf("\rexample: create-se control_se_bdcond.json se-1 i_bdcond o_rly01 50 10\n"); - return; - } - - char data_buf[MAX_FILE_SIZE]; - snprintf(data_buf, sizeof(data_buf), - "{ " - "\"id\": \"%s\", " - "\"input\": \"%s\", " - "\"output\": \"%s\", " - "\"priority\": \"459\", " - "\"dutyCycle\": \"%d\", " - "\"interval\": \"%d\" }", - argv[2], argv[3], argv[4], atoi(argv[5]), atoi(argv[6]) - ); - - bool status = GLOBAL_mdot->saveUserFile(argv[1], (void *)data_buf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d", __LINE__, status); - return; - } - - // send a message to the configuration handler to create the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_CREATE; - msg->control = CONTROL_SENSOR_ERROR; - strncpy(msg->controlFile, argv[1], sizeof(msg->controlFile)-1); - - printf("%s: Sending a create request for sensor error control %s type = %u\r\n", - __func__, msg->controlFile, msg->control); - - ConfigHandlerMailBox.put(msg); - printf("\r\n"); - return; -} - -/***************************************************************************** - * Function: cmd_createSetpoint - * Description: create control file - * - * @param argc-> number of args - * @param argv-> filename - * @return none - *****************************************************************************/ -void cmd_createSetpoint(int argc, char **argv) -{ - if ( argc != 8 ) { - printf("\rusage: create-sp <filename> <id> <input> <output> <sp> <dir> <tol>\n"); - printf("\rexample: create-sp control_sp_1.json bd i_cond o_rly1 2000 1 15\n"); - return; - } - - if ( strncmp(argv[1], CONTROL_SP_STR, strlen(CONTROL_SP_STR)) != 0 ) { - printf("\rFilename must be prefixed with control_sp_*\n"); - return; - } - - char data_buf[MAX_FILE_SIZE]; - snprintf(data_buf, sizeof(data_buf), - "{ " - "\"id\": \"%s\", " - "\"priority\": \"800\"," - "\"input\": \"%s\", " - "\"output\": \"%s\", " - "\"setpoint\": \"%s\"," - "\"prodfact\": \"100\"," - "\"actingDir\": \"%s\", " - "\"halert\": \"115\"," - "\"lalert\": \"85\", " - "\"hfs\": \"130\"," - "\"lfs\": \"70\", " - "\"tol\": \"%s\" }", argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); - - bool status = GLOBAL_mdot->saveUserFile(argv[1], (void *)data_buf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d", __LINE__, status); - return; - } - - // send a message to the configuration handler to create the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_CREATE; - msg->control = CONTROL_SETPOINT; - strncpy(msg->controlFile, argv[1], sizeof(msg->controlFile)-1); - - printf("%s: Sending a create request for control %s type = %u\r\n", - __func__, msg->controlFile, msg->control); - - ConfigHandlerMailBox.put(msg); - printf("\r\n"); - return; -} - -/***************************************************************************** - * Function: cmd_createTimer - * Description: create control file - * - * @param argc-> number of args - * @param argv-> filename - * @return none - *****************************************************************************/ -void cmd_createTimer(int argc, char **argv) -{ - if ( argc != 10 ) { - printf("\rusage: create-tm <filename> <id> <output> <day> <hh> <mm> <ss> <duration> <week>\n"); - printf("\rexample: create-tm control_tm_rly01.json o_rly01 wed 12 00 00 60\n"); - printf("\r <day> is sun, mon, tue, wed, thu, fri, sat\n"); - printf("\r <duration> is in seconds\n"); - printf("\r <week> is every, first, second, third, fourth, last, everyother\n\r\n"); - return; - } - - if ( strncmp(argv[1], CONTROL_TM_STR, strlen(CONTROL_TM_STR)) != 0 ) { - printf("\rFilename must be prefixed with control_tm_*\n"); - return; - } - - char data_buf[MAX_FILE_SIZE]; - snprintf(data_buf, sizeof(data_buf), - "{ " - "\"id\": \"%s\", " - "\"output\": \"%s\", " - "\"priority\": \"750\", " - "\"day\": \"%s\", " - "\"startHour\": \"%s\", " - "\"startMin\": \"%s\", " - "\"startSec\": \"%s\", " - "\"duration\": \"%s\", " - "\"week\": \"%s\" } ", - argv[2], argv[3], argv[4], argv[5], argv[6], - argv[7], argv[8], argv[9]); - - - bool status = GLOBAL_mdot->saveUserFile(argv[1], (void *)data_buf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)save file failed, status=%d", __LINE__, status); - return; - } - - // send a message to the configuration handler to create the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_CREATE; - msg->control = CONTROL_TIMER; - strncpy(msg->controlFile, argv[1], sizeof(msg->controlFile)-1); - - printf("%s: Sending a create request for control %s type = %u\r\n", - __func__, msg->controlFile, msg->control); - - ConfigHandlerMailBox.put(msg); - printf("\r\n"); - return; -} - -void cmd_createFailsafe(int argc, char **argv) -{ - char buf[MAX_FILE_SIZE]; - bool status; - - std::string filename = "control_fs_rly01.json"; - snprintf(buf, sizeof(buf), - "{" - "\"id\": \"BLOWDOWN-FS\", " - "\"input\": \"i_bdcond01\", " - "\"output\": \"o_rly01\", " - "\"priority\":\"700\", " - "\"lfsValue\":\"700\", " - "\"lfsDutyCycle\":\"0\", " - "\"lfsInterval\":\"1\", " - "\"hfsValue\":\"2700\", " - "\"hfsDutyCycle\":\"50\", " - "\"hfsInterval\":\"1\" " - "}"); - - status = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( status != true ) { - printf("\rFailed to create %s\n", filename.c_str()); - return; - } else { - printf("\r created %s\n", filename.c_str()); - } - - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_CREATE; - msg->control = CONTROL_FAILSAFE; - strncpy(msg->controlFile, filename.c_str(), sizeof(msg->controlFile)-1); - - printf("%s: Sending a create request for control %s type = %u\r\n", - __func__, msg->controlFile, msg->control); - - ConfigHandlerMailBox.put(msg); - Thread::wait(1000); - - filename = "control_fs_rly02.json"; - snprintf(buf, sizeof(buf), - " {" - "\"id\": \"TRASAR-FS\", " - "\"input\": \"i_tra01\", " - "\"output\": \"o_rly02\", " - "\"priority\":\"700\", " - "\"lfsValue\":\"70\", " - "\"lfsDutyCycle\":\"20\", " - "\"lfsInterval\":\"15\", " - "\"hfsValue\":\"200\", " - "\"hfsDutyCycle\":\"0\", " - "\"hfsInterval\":\"15\" " - "}"); - - status = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( status != true ) { - printf("\rFailed to create %s\n", filename.c_str()); - return; - } else { - printf("\r created %s\n", filename.c_str()); - } - - msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_CREATE; - msg->control = CONTROL_FAILSAFE; - strncpy(msg->controlFile, filename.c_str(), sizeof(msg->controlFile)-1); - - printf("%s: Sending a create request for control %s type = %u\r\n", - __func__, msg->controlFile, msg->control); - - ConfigHandlerMailBox.put(msg); -} - -/***************************************************************************** - * Function: cmd_logLevel - * Description: get or set the current log-level - * - * @param argc (not used) - * @param argv (not used) - * @return none - *****************************************************************************/ -void cmd_logLevel(int argc, char **argv) -{ - uint8_t logLevel = 0; - - const char *mapper[] = { "NONE", - "FATAL", - "ERROR", - "WARNING", - "INFO", - "DEBUG", - "TRACE" - }; - - if ( argc == 1 ) { - printf("\r current log-level set to %s\r\n", - mapper[ICELog::getLogLevel()]); - goto usage; - } - - if ( argc != 2 ) { -usage: - printf("\rusage: log-level [0-6]\n"); - printf("\r 0 = NONE\n"); - printf("\r 1 = FATAL\n"); - printf("\r 2 = ERROR\n"); - printf("\r 3 = WARNING\n"); - printf("\r 4 = INFO\n"); - printf("\r 5 = DEBUG\n"); - printf("\r 6 = TRACE\r\n"); - return; - } - - logLevel = atoi(argv[1]); - if ( logLevel > 6 ) - goto usage; - - // reassign the log level - printf("...setting log-level to %s\r\n", mapper[logLevel]); - ICELog::setLogLevel(logLevel); - printf("\r\n"); -} - - -/***************************************************************************** - * Function: cmd_vregmap - * Description: show virtual register map - * - * @param argc-> number of args - * @param argv-> filename - * @return none - *****************************************************************************/ -extern std::map<std::string,RegisterValue> RegisterValueMap; - -void cmd_vregmap(int argc, char **argv) -{ - UNUSED(argc); - UNUSED(argv); - - std::map<std::string, RegisterValue>::iterator iter; - for (iter = RegisterValueMap.begin(); iter != RegisterValueMap.end(); ++iter) { - printf("id=%s, value=%.4f\r\n", iter->first.c_str(), iter->second.float_value ); - } -} - -void cmd_testPreBleed(int argc, char **argv) -{ - char buf[MAX_FILE_SIZE]; - - // OUTPUT (virtual): variable set to TRUE when pre-bleed is done - std::string filename = "vreg_preBleedDone.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"v_preBleedDone\", " - "\"value\": \"0\" " - "}"); - bool rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // OUTPUT (virtual): variable is set to TRUE while pre-bleed time is running via timer control - filename = "vreg_pbTimer.json"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"v_pbTimer\", " - "\"value\": \"0\" " - "}"); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // JavaScript Executor: - // IN: o_rly01 - // IN: v_pbTimer - // OUT: v_preBleedDone - // - // Description: execute the js_nor_regs() script with arguments o_rly01 - // and v_pbTimer; stores the result in v_preBleedDone - // - filename = "exe_js_prebleed_done.js"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"PREBLEED_DONE\"," - "\"script\":\"nor_regs\"," - "\"args\":[{\"arg\":\"o_rly01\"},{\"arg\":\"v_pbTimer\"},{\"arg\":\"v_preBleedDone\"}]" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // JavaScript: nor_regs(a, b) => c - filename = "js_nor_regs.js"; - snprintf(buf, sizeof(buf), - "var nor_regs = function(a, b, c) {" - "res1 = getRegister(a);" - "res2 = getRegister(b);" - "if ( res1 == 0 || res2 == 0 ) { setRegister(c, 1); }" - "return 1;" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } -} - -/***************************************************************************** - * Function: cmd_setTime - * Description: set real-time clock - * - * @param argc-> number of args - * @param argv-> input - * @return none - *****************************************************************************/ - - -void cmd_setTime(int argc, char **argv) -{ - if ( argc != 7 ) { - printf("\rusage: set-time <yyyy> <mm> <dd> <hh> <mm> <ss>\n"); - printf("\rexample: set-time 2017 1 24 9 0 0\r\n"); - return; - } - - int isc = atoi( argv[6] ); // sec - int imn = atoi( argv[5] ); // min - int ihr = atoi( argv[4] ); // hour - int idy = atoi( argv[3] ); // idy - int imo = atoi( argv[2] ); // imo - int iyr = atoi( argv[1] ); // year - -// printf( "\r\n%s:%d: sec(0x%x) min(0x%x) hour(0x%x) day(0x%x) mon(0x%x) year(0x%x)\r\n", __func__, __LINE__, isc, imn, ihr, idy, imo, iyr ); - - rtc_set_time(iyr, imo, idy, ihr, imn, isc); -} -void getTime() -{ - char time_string[80]; - struct tm rtc_time; - struct tm * ts; - time_t curr_sec; - int year = 0; - - rtc_get_time(&year, &rtc_time.tm_mon, &rtc_time.tm_mday, &rtc_time.tm_hour, &rtc_time.tm_min, &rtc_time.tm_sec); -// printf( "\r\n%s:%d:sec(0x%x) min(0x%x) hour(0x%x) day(0x%x) mon(0x%x) year(0x%x)\r\n", __func__, __LINE__, rtc_time.tm_sec, rtc_time.tm_min, rtc_time.tm_hour, rtc_time.tm_mday, rtc_time.tm_mon, year ); - - rtc_time.tm_mon = rtc_time.tm_mon - 1; - rtc_time.tm_year = year - 1900; - curr_sec = mktime( &rtc_time ); - - ts = localtime(&curr_sec); - strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", ts); - - printf("%s\r\n", time_string); -} - -void cmd_getTime(int argc, char **argv) -{ - getTime(); -} -
--- a/ICE-Application/src/CommandParser/cmd.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/* - * =============================================================== - * Natural Tiny Shell (NT-Shell) Application example. - * Version 0.0.6 - * =============================================================== - * Copyright (c) 2010-2011 Shinichiro Nakamura - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * =============================================================== - */ - -#ifndef CMD_H -#define CMD_H - -// debug commands -void cmd_debug_fs (int argc, char **argv); -void cmd_debug_mn (int argc, char **argv); -void cmd_debug_se (int argc, char **argv); -void cmd_debug_seq (int argc, char **argv); -void cmd_debug_sp (int argc, char **argv); -void cmd_debug_tm (int argc, char **argv); -// - -void cmd_cif (int argc, char **argv); -void cmd_cof (int argc, char **argv); -void cmd_destroy (int argc, char **argv); - -void cmd_createCAlg (int argc, char **argv); -void cmd_createManual (int argc, char **artv); -void cmd_createSError (int argc, char **argv); -void cmd_createSetpoint (int argc, char **argv); -void cmd_createTimer (int argc, char **argv); -void cmd_createFailsafe (int argc, char **argv); -void cmd_createVreg (int argc, char **argv); -void cmd_createSeq (int argc, char **argv); - -void cmd_version (int argc, char **argv); -void cmd_help (int argc, char **argv); -void cmd_json (int argc, char **argv); -void cmd_v7 (int argc, char **argv); -void cmd_heap_stats (int argc, char **argv); -void cmd_outputs (int argc, char **argv); -void cmd_stack (int argc, char **argv); -void cmd_readFile (int argc, char **argv); -void cmd_writeFile (int argc, char **argv); -void cmd_deleteFile (int argc, char **argv); -void cmd_lsFile (int argc, char **argv); -void cmd_formatFS (int argc, char **argv); -void cmd_createSequence (int argc, char **argv); -void cmd_preload (int argc, char **argv); -void cmd_reset (int argc, char **argv); -void cmd_modmap (int argc, char **argv); -void cmd_ShowControls (int argc, char **argv); -void cmd_ShowTimers (int argc, char **argv); -void cmd_ShowManuals (int argc, char **argv); -void cmd_ShowFailsafes (int argc, char **argv); -void cmd_ShowComposites (int argc, char **argv); -void cmd_ShowSensorErrors(int argc, char **argv); -void cmd_ShowSetpoints (int argc, char **argv); -void cmd_ShowSequences (int argc, char **argv); -void cmd_ShowAlgorithms (int argc, char **argv); -void cmd_simerr (int argc, char **argv); -void cmd_simin (int argc, char **argv); -void cmd_simall (int argc, char **argv); -void cmd_logLevel (int argc, char **argv); -void cmd_testLog (int argc, char **argv); -void cmd_spiTest (int argc, char **argv); -void cmd_vregmap (int argc, char **argv); -void cmd_testPreBleed (int argc, char **argv); -void cmd_getTime (int argc, char **argv); -void cmd_setTime (int argc, char **argv); - -#endif
--- a/ICE-Application/src/CommandParser/cmd_utils.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,125 +0,0 @@ -#include "cmd_utils.h" -#include "global.h" - - -// create a virtual register -void createVregFile(VregFile_t &vreg_f) -{ - char buf[MAX_FILE_SIZE]; - snprintf(buf, sizeof(buf), - "{" - "\"id\":\"%s\", " - "\"value\":\"%f\" }", vreg_f.id.c_str(), vreg_f.value); - bool rc = GLOBAL_mdot->saveUserFile(vreg_f.fname.c_str(), - (void*)buf, - MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", vreg_f.fname.c_str()); - } else { - printf("\r...generated %s\n", vreg_f.fname.c_str()); - } -} - -// create an input/output file -void createIOFile(IOFile_t &io_file) -{ - char buf[MAX_FILE_SIZE]; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"%s\", " - "\"name\": \"%s\", " - "\"units\": \"PPM\", " - "\"min\": \"%f\", " - "\"max\": \"%f\", " - "\"node\": \"%u\", " - "\"reg\": \"%u\", " - "\"rtype\": \"%u\", " - "\"type\": \"%u\", " - "\"size\": \"%u\", " - "\"order\": \"%u\", " - "\"rfreq\": \"%u\", " - "\"cmd\": \"\"," - "\"args\": []," - "\"fmt\": \"%%.2f\" } ", io_file.id.c_str(), - io_file.name.c_str(), - io_file.min, - io_file.max, - io_file.node, - io_file.reg, - io_file.rtype, - io_file.type, - io_file.size, - io_file.order, - io_file.rfreq); - bool rc = GLOBAL_mdot->saveUserFile(io_file.fname.c_str(), (void *)buf, MAX_FILE_SIZE); - if( rc != true ) { - printf("(%d)save file failed, status=%d", __LINE__, rc); - } else { - printf("\r...generated %s\n", io_file.fname.c_str()); - } -} - -// create a setpoint control file -void createSetpointControlFile(SetpointControlFile_t &sp_f) -{ - char buf[MAX_FILE_SIZE]; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"%s\", " - "\"priority\": \"%u\"," - "\"input\": \"%s\", " - "\"output\": \"%s\", " - "\"setpoint\": \"%f\"," - "\"prodfact\": \"%f\"," - "\"actingDir\": \"%u\", " - "\"tol\": \"%f\" }", sp_f.id.c_str(), - sp_f.priority, - sp_f.input.c_str(), - sp_f.output.c_str(), - sp_f.setpoint, - sp_f.productFactor, - sp_f.actingDir, - sp_f.tolerance); - bool rc = GLOBAL_mdot->saveUserFile(sp_f.controlFile.c_str(), - (void *)buf, MAX_FILE_SIZE); - if( rc != true ) { - printf("(%d)save file failed, status=%d", __LINE__, rc); - return; - } else { - printf("\r...generated %s\n", sp_f.controlFile.c_str()); - } -} - -// create a timer control file -void createTimerControlFile(TimerControlFile_t &timer_f) -{ - char buf[MAX_FILE_SIZE]; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"%s\", " - "\"output\": \"%s\", " - "\"priority\": \"%u\", " - "\"day\": \"%u\", " - "\"startHour\": \"%u\", " - "\"startMin\": \"%u\", " - "\"startSec\": \"%u\", " - "\"duration\": \"%u\", " - "\"week\": \"%u\" } ", - timer_f.id.c_str(), - timer_f.output.c_str(), - timer_f.priority, - timer_f.day, - timer_f.startHour, - timer_f.startMin, - timer_f.startSec, - timer_f.duration, - timer_f.week); - bool rc = GLOBAL_mdot->saveUserFile(timer_f.controlFile.c_str(), (void *)buf, MAX_FILE_SIZE); - if( rc != true ) { - printf("(%d)save file failed, status=%d", __LINE__, rc); - return; - } else { - printf("\r...generated %s\n", timer_f.controlFile.c_str()); - } -} -
--- a/ICE-Application/src/CommandParser/cmd_utils.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -#ifndef CMDUTILS_H -#define CMDUTILS_H - -#include "global.h" -#include <string> -#include <stdio.h> -#include <stdint.h> - -typedef struct vregFile_tag { - std::string fname; - std::string id; - float value; -} VregFile_t; - -typedef struct ioFile_tag { - std::string fname; - std::string id; - std::string name; - float min; - float max; - unsigned char node; - unsigned char rtype; - unsigned char type; - unsigned char size; - unsigned char order; - unsigned char rfreq; - unsigned char argc; - uint32_t reg; - RegisterType_t regType; -} IOFile_t; - -typedef struct setpointControlFile_tag { - std::string controlFile; - std::string id; // control identifier - int priority; // control priority - std::string input; // control input - std::string output; // control output - double setpoint; // setpoint value - double productFactor; // unused - bool actingDir; // acting direction - double tolerance; - -} SetpointControlFile_t; - -typedef struct timerControlFile_tag { - std::string controlFile; // file containing control data - std::string id; // timer identifier - std::string output; // output to control - unsigned int priority; // control priority - unsigned int day; - unsigned int startHour; // start hour (0-23) - unsigned int startMin; // start minute (0-59) - unsigned int startSec; // start second (0-59) - unsigned int duration; // duration in seconds - unsigned int week; // every week, first week, ... -} TimerControlFile_t; - -void createVregFile(VregFile_t&); -void createIOFile(IOFile_t&); -void createSetpointControlFile(SetpointControlFile_t&); -void createTimerControlFile(TimerControlFile_t&); -#endif
--- a/ICE-Application/src/CommandParser/sequence.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,216 +0,0 @@ -#include "cmd_utils.h" -#include "cmd.h" - -void cmd_createSequence(int argc, char **argv) -{ - // - // This command will test the basic functionality of a sequence control - // - // virtual registers: - // v_slugSchedule -> start trigger for the slug dose - // v_alwaysTrue -> always set to 1 - // v_preBleedDone -> set to 1 when prebleed is done - // v_slugFeedDone -> set to 1 when the slug-feed is done - // v_pbTimer -> prebleed timer control output, set to 0 when finished - // - // physical outputs: - // o_rly01 -> blowdown controlled (higher pri setpoint) - // o_rly02 -> slug dose relay - // - // scripts: - // js_nor_regs.js -> performs nor operation of args a, b - // - // timer controls: - // pb-guard-timer -> prebleed guard timer - // - // setpoint controls: - // pre-bleed-sp -> pre-bleed setpoint control - // slug-dose-sp -> slug dose setpoint control - - // vreg: slug-schedule - VregFile_t vreg; - vreg.fname = "vreg_slugScheduleOn.json"; - vreg.id = "v_slugScheduleOn"; - vreg.value = 0; - createVregFile(vreg); - - // vreg: always true - vreg.fname = "vreg_alwaysTrue.json"; - vreg.id = "v_alwaysTrue"; - vreg.value = 1; - createVregFile(vreg); - - // vreg: pre-bleed done - vreg.fname = "vreg_preBleedDone.json"; - vreg.id = "v_preBleedDone"; - vreg.value = 0; - createVregFile(vreg); - - // vreg: slug-feed done - vreg.fname = "vreg_slugFeedDone.json"; - vreg.id = "v_slugFeedDone"; - vreg.value = 0; - createVregFile(vreg); - - // vreg: prebleed timer - vreg.fname = "vreg_pbTimer.json"; - vreg.id = "v_preBleedTimer"; - vreg.value = 0; - createVregFile(vreg); - - // Blowdown Control --------------------------------------------------- - // input - IOFile_t io_file; - memset(&io_file, 0, sizeof(IOFile_t)); - io_file.fname = "input_i_bdcond01.json"; - io_file.id = "i_bdcond01"; - io_file.name = "Tower Conductivity"; - io_file.min = 0; - io_file.max = 6000; - io_file.node = 21; - io_file.reg = 5; - io_file.type = 0; - io_file.regType = REG_TYPE_INPUT; - io_file.size = 2; - io_file.order = 2; - io_file.rfreq = 5; - createIOFile(io_file); - - // output - memset(&io_file, 0, sizeof(io_file)); - io_file.fname = "output_o_rly01.json"; - io_file.id = "o_rly01"; - io_file.name = "Blowdown-Relay"; - io_file.min = 0; - io_file.max = 100; - io_file.node = 0; - io_file.reg = 1; - io_file.type = 0; - io_file.regType = REG_TYPE_OUTPUT; - io_file.size = 2; - io_file.order = 2; - io_file.rfreq = 5; - createIOFile(io_file); - - // blowdown control - SetpointControlFile_t sp; - memset(&sp, 0, sizeof(sp)); - sp.controlFile = "seq_slug_dose_control_sp_prebleed.json"; - sp.actingDir = 1; - sp.id = "blowdown"; - sp.setpoint = 1750; - sp.input = "i_bdcond01"; - sp.output = "o_rly01"; - sp.priority = 750; // this is higher pri than a regular sp control - sp.tolerance = 30; - createSetpointControlFile(sp); - - // timer - TimerControlFile_t timer; - memset(&timer, 0, sizeof(timer)); - timer.controlFile = "seq_slug_dose_control_tm_prebleed.json"; - timer.priority = 750; - timer.output = "v_preBleedTimer"; - timer.id = "prebleed"; - timer.duration = 30; - timer.day = 0; - createTimerControlFile(timer); - - // sequence control - std::string filename = "control_seq_slug_dose.json"; - char buf[MAX_FILE_SIZE * 3]; - snprintf(buf, sizeof(buf), - "{ " - "\"id\": \"slug_dose\", " - "\"startTrigger\": \"v_slugScheduleOn\", " - "\"sequence\": " - "[" - // START PRE-BLEED elements - "{ \"startTrigger\":\"v_alwaysTrue\", " - "\"actions\": " - "[" - "{ \"action\": \"assign\", \"id\": \"v_preBleedTimer\", \"val\":\"1.0\" }," - "{ \"action\": \"assign\", \"id\": \"v_preBleedDone\", \"val\":\"0.0\" }," - "{ \"action\": \"createsp\", \"id\": \"prebleed\" }," - "{ \"action\": \"createtm\", \"id\": \"prebleed\" }" - "]," // end actions array - "\"stopTrigger\": \"v_preBleedDone\" }," - - // DELETE PRE-BLEED elements - "{ \"startTrigger\":\"v_alwaysTrue\", " - "\"actions\": " - "[" - "{ \"action\": \"deletesp\", \"id\": \"prebleed\" }, " - "{ \"action\": \"deletetm\", \"id\": \"prebleed\" }" - "]," - "\"stopTrigger\": \"v_alwaysTrue\" }" -#if 0 - // SLUG-FEED elements - "{ \"startTrigger\":\"v_alwaysTrue\", " - "\"actions\": " - "[" - "{ \"action\": \"createsp\", \"id\": \"SLUG-FEED-SP\" } " - "]," - "\"stopTrigger\": \"v_slugFeedDone\" }," - - // DELETE SLUG-FEED elements - "{ \"startTrigger\":\"v_alwaysTrue\", " - "\"actions\": " - "[" - "{ \"action\": \"deletesp\", \"id\": \"SLUG-FEED-SP\" } " - "]," - "\"stopTrigger\": \"v_alwaysTrue\" }" -#endif - "] " // end sequence array - "} "); - printf("\r[DEBUG: bytes = %u\n", (size_t)strlen(buf)); - bool rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, sizeof(buf)); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // JavaScript Executor: - // IN: o_rly01 - // IN: v_preBleedTimer - // OUT: v_preBleedDone - // - // Description: execute the js_nor_regs() script with arguments o_rly01 - // and v_pbTimer; stores the result in v_preBleedDone - // - filename = "exe_js_prebleed_done.js"; - snprintf(buf, sizeof(buf), - "{ " - "\"id\":\"PREBLEED_DONE\"," - "\"script\":\"nor_regs\"," - "\"args\":[{\"arg\":\"o_rly01\"},{\"arg\":\"v_preBleedTimer\"},{\"arg\":\"v_preBleedDone\"}]" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } - - // JavaScript: nor_regs(a, b) => c - filename = "js_nor_regs.js"; - snprintf(buf, sizeof(buf), - "var nor_regs = function(a, b, c) {" - "res1 = getRegister(a);" - "res2 = getRegister(b);" - "if ( res1 == 0 || res2 == 0 ) { setRegister(c, 1); }" - "return 1;" - "};" - ); - rc = GLOBAL_mdot->saveUserFile(filename.c_str(), (void *)buf, MAX_FILE_SIZE); - if ( rc != true ) { - printf("\rFailed to save %s\n", filename.c_str()); - return; - } else { - printf("\r...generated %s\n", filename.c_str()); - } -}
--- a/ICE-Application/src/ConfigurationHandler/Algorithms/CompositeAlgorithm.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,118 +0,0 @@ -/****************************************************************************** - * - * File: CompositeAlgorithm.cpp - * Desciption: source for the ICE Composite Algorithm - * - *****************************************************************************/ -#include "global.h" -#include "cJSON.h" -#include "ICELog.h" -#include "CompositeAlgorithm.h" -//#include "../add-ons/MTSLog/MTSLog.h" - -// -// method: load -// description: load the pertinents from the control file -// -// @param[in] _controlFile : name of the control file -// @param[out] none -// @return none -// -bool CompositeAlgorithm::load(const std::string _controlFile) -{ - controlFile = _controlFile; - - // read the data into a buffer - char dataBuf[MAX_FILE_SIZE]; - bool rc = GLOBAL_mdot->readUserFile(controlFile.c_str(), (void *)dataBuf, sizeof(dataBuf)); - if ( rc != true ) { - logError("%s: failed to read %s", __func__, controlFile.c_str()); - // caller should destroy the object - return false; - } - - // parse the JSON data - cJSON * root = cJSON_Parse(dataBuf); - - if ( !cJSON_HasObjectItem(root, "id") || - !cJSON_HasObjectItem(root, "opr") || - !cJSON_HasObjectItem(root, "op") || - !cJSON_HasObjectItem(root, "true") || - !cJSON_HasObjectItem(root, "false") ) { - logError("%s: control file is missing expected tags", __func__); - cJSON_Delete(root); - return false; - } - - id = cJSON_GetObjectItem(root, "id")->valuestring; - opr = cJSON_GetObjectItem(root, "opr")->valuestring; - op = cJSON_GetObjectItem(root, "op")->valuestring; - resultTrue = cJSON_GetObjectItem(root, "true")->valuestring; - resultFalse = cJSON_GetObjectItem(root, "false")->valuestring; - - cJSON_Delete(root); - return true; -} - -// -// method: validateControlData -// description: validate the JSON formatted string -// -// @param[in] buf -> JSON formatted string -// @param[out] none -// @return true if valid; false otherwise -// -bool CompositeAlgorithm::validateControlData(const char *buf) -{ - bool rc = true; - cJSON * root = cJSON_Parse(buf); - - if ( !cJSON_HasObjectItem(root, "id") || - !cJSON_HasObjectItem(root, "opr") || - !cJSON_HasObjectItem(root, "op") || - !cJSON_HasObjectItem(root, "true") || - !cJSON_HasObjectItem(root, "false") ) { - logError("%s: control file is missing expected tags", __func__); - rc = false; - } - cJSON_Delete(root); - return rc; -} - -// -// method: copyControlData -// description: copy JSON data to control data -// -// @param[in] buf -> JSON formatted string -// @param[out] none -// @return -void CompositeAlgorithm::copyControlData(const char *buf) -{ - cJSON * root = cJSON_Parse(buf); - - id = cJSON_GetObjectItem(root, "id")->valuestring; - opr = cJSON_GetObjectItem(root, "opr")->valuestring; - op = cJSON_GetObjectItem(root, "op")->valuestring; - resultTrue = cJSON_GetObjectItem(root, "true")->valuestring; - resultFalse = cJSON_GetObjectItem(root, "false")->valuestring; - - cJSON_Delete(root); -} - -// -// method: display -// description: display the pertinents -// -// @param[in] none -// @param[out] none -// @return none -// -void CompositeAlgorithm::display(void) -{ - printf("\r id : %s\n", id.c_str()); - printf("\r opr : %s\n", opr.c_str()); - printf("\r op : %s\n", op.c_str()); - printf("\r true : %s\n", resultTrue.c_str()); - printf("\r false : %s\n", resultFalse.c_str()); - printf("\r\n"); -} \ No newline at end of file
--- a/ICE-Application/src/ConfigurationHandler/Algorithms/CompositeAlgorithm.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/****************************************************************************** - * - * File: CompositeAlgorithm.h - * Desciption: interface file for the ICE Composite Algorithm - * - *****************************************************************************/ -#ifndef COMPOSITEALGORITHM_H -#define COMPOSITEALGORITHM_H - -#include <stdio.h> -#include <string> - -class CompositeAlgorithm -{ -private: - std::string controlFile; // the control file - std::string id; // algorithm id - std::string tag; // input (real or virtual) - std::string opr; // right operand - std::string op; // operation to perform (+, -, ==, etc.) - std::string resultTrue; // what to do when true - std::string resultFalse; // what to do when false - - bool validateControlData(const char *buf); - void copyControlData(const char *buf); -public: - CompositeAlgorithm() {} - ~CompositeAlgorithm() { - printf("%s invoked", __func__); - } - - bool load(const std::string); - - void display(void); - - std::string getId(void) const { - return id; - } - std::string getTag(void) const { - return tag; - } - std::string getOpr(void) const { return opr; } - std::string getOp(void) const { return op; } - std::string getResultTrue(void) const { return resultTrue; } - std::string getResultFalse(void) const { return resultFalse; } -}; - -#endif -
--- a/ICE-Application/src/ConfigurationHandler/ConfigurationHandler.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,733 +0,0 @@ -/****************************************************************************** - * - * File: ConfigurationHandler.cpp - * Desciption: source for the ICE Configuration Handler - * - *****************************************************************************/ -#include "ConfigurationHandler.h" -#include "global.h" -#include "SetpointControl.h" -#include "TimerControl.h" -#include "CompositeControl.h" -#include "CompositeAlgorithm.h" -#include "SensorErrorControl.h" -#include "cJSON.h" -#include "utilities.h" -#include "ICELog.h" -#include <algorithm> -#include <stdlib.h> - -// -// The control maps -//StringPIDMap PIDTable; // PID control object table -StringSetpointMap setpointTable; // setpoint control object table -StringTimerMap timerTable; // timer control object table -StringManualMap manualTable; // manual control object table -StringCompositeMap compositeTable; // composite control object table -StringFailsafeMap failsafeTable; // failsafe control object table -StringAlgorithmMap algorithmTable; // composite control algorithms -StringSequenceMap sequenceTable; // sequence control object table -StringSensorErrorMap sensorErrorTable; // sensor error object table - -Mutex manual_mutex; -Mutex setpoint_mutex; -Mutex timer_mutex; -Mutex failsafe_mutex; -Mutex composite_mutex; -Mutex sequence_mutex; -Mutex sensorError_mutex; - -// local function prototypes -static int loadPersistentControls(void); // load the controls on flash - -// local helper functions -static int createControl(const ConfigMessage_t *msg); -static int modifyControl(const ConfigMessage_t *msg); -static int destroyControl(const ConfigMessage_t *msg); - -static unsigned int getManualControlType(const char *filename); - -/***************************************************************************** - * Function: ConfigurationHandler() - * Description: The ICE Configuration Handler - * - * @param args (unused) - * @return none - *****************************************************************************/ -void ConfigurationHandler(void const *args) -{ - (void)(args); -#ifdef LOAD_PERSISTENT_CONFIGURATIONS - loadPersistentControls(); -#endif - osSignalSet(mainThreadId, sig_control_continue); - - printf("\rConfigurationHandler has started...\n"); - - while ( true ) { - // wait for an event - osEvent evt = ConfigHandlerMailBox.get(); - if (evt.status == osEventMail) { - ConfigMessage_t *msg = (ConfigMessage_t*) evt.value.p; - - logInfo("\r%s: msg->action = %d\n", __func__, msg->action); - logInfo("\r%s: msg->control = %d\n", __func__, msg->control); - logInfo("\r%s: msg->controlFile = %s\n", __func__, msg->controlFile); - - switch ( msg->action ) { - case ACTION_CREATE: { - (void)createControl(msg); - break; - } - case ACTION_MODIFY: { - (void)modifyControl(msg); - break; - } - case ACTION_DESTROY: { - (void)destroyControl(msg); - break; - } - default: - break; - } - - // free the message - ConfigHandlerMailBox.free(msg); - } - } -} - -// -// function: ConfigurationHandler_showSetpointControls -// description: display contents of the setpoint control table -// -void ConfigurationHandler_showSetpointControls(void) -{ - setpoint_mutex.lock(); - if ( !setpointTable.empty() ) { - printf("\r\n"); - StringSetpointMap::const_iterator pos; - for ( pos = setpointTable.begin(); pos != setpointTable.end(); ++pos ) { - pos->second->display(); - } - } - setpoint_mutex.unlock(); -} - -// -// function: ConfigurationHandler_showManualControls -// description: display contents of the manual control table -// -void ConfigurationHandler_showManualControls(void) -{ - manual_mutex.lock(); - if ( !manualTable.empty() ) { - printf("\r\n"); - StringManualMap::const_iterator pos; - for ( pos = manualTable.begin(); pos != manualTable.end(); ++pos ) { - pos->second->display(); - } - } - manual_mutex.unlock(); -} - -// -// function: ConfigurationHandler_showTimerControls -// description: display contents of the timer control table -// -void ConfigurationHandler_showTimerControls(void) -{ - timer_mutex.lock(); - if ( !timerTable.empty() ) { - printf("\r\n"); - StringTimerMap::const_iterator pos; - for ( pos = timerTable.begin(); pos != timerTable.end(); ++pos ) { - pos->second->display(); - } - } - timer_mutex.unlock(); -} - -// -// function: ConfigurationHandler_showCompositeControls -// description: display contents of the composite control table -// -void ConfigurationHandler_showCompositeControls(void ) -{ - composite_mutex.lock(); - if ( !compositeTable.empty() ) { - printf("\r\n"); - StringCompositeMap::const_iterator pos; - for ( pos = compositeTable.begin(); pos != compositeTable.end(); ++pos) { - pos->second->display(); - } - } - composite_mutex.unlock(); -} - -// -// function: ConfigurationHandler_showFailsafeControls -// description: display contents of the failsafe control table -// -// @param[in] none -// @param[out] none -// @return none -// -void ConfigurationHandler_showFailsafeControls(void) -{ - failsafe_mutex.lock(); - if ( !failsafeTable.empty() ) { - printf("\r\n"); - StringFailsafeMap::const_iterator pos; - for ( pos = failsafeTable.begin(); pos != failsafeTable.end(); ++pos ) { - pos->second->display(); - } - } - failsafe_mutex.unlock(); -} - -// -// function: ConfigurationHandler_showFailsafeControls -// description: display contents of the failsafe control table -// -// @param[in] none -// @param[out] none -// @return none -// -void ConfigurationHandler_showSensorErrorControls(void) -{ - sensorError_mutex.lock(); - if ( !sensorErrorTable.empty() ) { - printf("\r\n"); - StringSensorErrorMap::const_iterator pos; - for ( pos = sensorErrorTable.begin(); pos != sensorErrorTable.end(); ++pos ) { - pos->second->display(); - } - } - sensorError_mutex.unlock(); -} - - -// -// function: ConfigurationHandler_showSequenceControls() -// description: display contents of the sequence control table -// -// @param[in] none -// @param[out] none -// @return none -// -void ConfigurationHandler_showSequenceControls(void) -{ - sequence_mutex.lock(); - if ( !sequenceTable.empty() ) { - printf("\r\n"); - StringSequenceMap::const_iterator pos; - for ( pos = sequenceTable.begin(); pos != sequenceTable.end(); ++pos ) { - pos->second->display(); - } - } - sequence_mutex.unlock(); -} - -// -// Function: ConfigurationHandler_showControls() -// Description: show the controls -// -// @param[in] none -// @return none -// -void ConfigurationHandler_showControls(void) -{ - ConfigurationHandler_showSetpointControls(); - ConfigurationHandler_showTimerControls(); - ConfigurationHandler_showManualControls(); - ConfigurationHandler_showFailsafeControls(); - ConfigurationHandler_showCompositeControls(); - ConfigurationHandler_showSequenceControls(); - ConfigurationHandler_showSensorErrorControls(); - // TODO: PID controls -} - -// -// function: ConfigurationHandler_showAlgorithms -// description: display the control algorithms -// -// @param none -// @return none -// -void ConfigurationHandler_showAlgorithms(void) -{ - StringAlgorithmMap::const_iterator pos; - - for ( pos = algorithmTable.begin(); pos != algorithmTable.end(); ++pos ) { - pos->second->display(); - } -} - -/***************************************************************************** - * Function: loadPersistentControls() - * Description: load persistent controls from flash - * - * @param none - * @return none - *****************************************************************************/ -static int loadPersistentControls(void) -{ - static bool loaded = false; - - if ( !loaded ) { // lazy protection - - printf("\rLoading persistent controls: \n"); - std::vector<std::string> file_list = GLOBAL_mdot->listUserFiles(); - - loaded = true; - - for (std::vector<std::string>::const_iterator i = file_list.begin(); i != file_list.end(); ++i) { - if( strncmp( i->c_str(), CONTROL_SP_STR, strlen(CONTROL_SP_STR)) == 0 ) { - // create the setpoint control - ConfigMessage_t msg; - msg.control = CONTROL_SETPOINT; - strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile)); - int rc = createControl(&msg); - if ( rc != 0 ) { - logError("%s: failed to load %s", __func__, msg.controlFile); - } else { - printf("\r setpoint control %s loaded.\n", msg.controlFile); - } - } else if ( strncmp( i->c_str(), CONTROL_TM_STR, strlen(CONTROL_TM_STR)) == 0 ) { - // create the timer control - ConfigMessage_t msg; - msg.control = CONTROL_TIMER; - strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile)); - int rc = createControl(&msg); - if ( rc != 0 ) { - logError("%s: failed to load %s", __func__, msg.controlFile); - - } else { - printf("\r timer control %s loaded.\n", msg.controlFile); - } - } else if ( strncmp( i->c_str(), CONTROL_COMP_STR, strlen(CONTROL_COMP_STR)) == 0 ) { - ConfigMessage_t msg; - msg.control = CONTROL_COMPOSITE; - strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile)); - int rc = createControl(&msg); - if ( rc != 0 ) { - logError("%s: failed to load %s\n", __func__, msg.controlFile); - } else { - printf("\r composite control %s loaded.\n", msg.controlFile); - } - } else if ( strncmp( i->c_str(), CONTROL_CA_STR, strlen(CONTROL_CA_STR)) == 0 ) { - ConfigMessage_t msg; - msg.control = CONTROL_ALGORITHM; - strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile)); - int rc = createControl(&msg); - if ( rc != 0 ) { - logError("%s: failed to load %s\n", __func__, msg.controlFile); - } else { - printf("\r algorithmic control %s loaded.\n", msg.controlFile); - } - } else if ( strncmp( i->c_str(), CONTROL_FS_STR, strlen(CONTROL_FS_STR)) == 0 ) { - ConfigMessage_t msg; - msg.control = CONTROL_FAILSAFE; - strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile)); - int rc = createControl(&msg); - if ( rc != 0 ) { - logError("%s: failed to load %s\n", __func__, msg.controlFile); - } else { - printf("\r failsafe control %s loaded.\n", msg.controlFile); - } - } else if ( strncmp( i->c_str(), CONTROL_PID_STR, strlen(CONTROL_PID_STR)) == 0 ) { - // TODO: - } else if ( strncmp( i->c_str(), CONTROL_MN_STR, strlen(CONTROL_MN_STR)) == 0 ) { - // TODO: delete any timed manual control, not continuous... - if ( getManualControlType(i->c_str()) == MANUAL_CONTROL_TYPE_CONTINUOUS ) { - ConfigMessage_t msg; - msg.control = CONTROL_MANUAL; - strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile)); - int rc = createControl(&msg); - if ( rc != 0 ) { - logError("%s: failed to load %s\n", __func__, msg.controlFile); - } else { - printf("\r manual control %s loaded\n", msg.controlFile); - } - } else { - GLOBAL_mdot->deleteUserFile(i->c_str()); - } - } else if ( strncmp( i->c_str(), CONTROL_SEQ_STR, strlen(CONTROL_SEQ_STR)) == 0 ) { - ConfigMessage_t msg; - msg.control = CONTROL_SEQUENCE; - strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile)); - int rc = createControl(&msg); - if ( rc != 0 ) { - logError("%s: failed to load %s\n", __func__, msg.controlFile); - } else { - printf("\r sequence control %s loaded.\n", msg.controlFile); - } - } else if ( strncmp( i->c_str(), CONTROL_SE_STR, strlen(CONTROL_SE_STR)) == 0 ) { - ConfigMessage_t msg; - msg.control = CONTROL_SENSOR_ERROR; - strncpy(msg.controlFile, i->c_str(), sizeof(msg.controlFile)); - int rc = createControl(&msg); - if ( rc != 0 ) { - logError("%s: failed to load %s\n", __func__, msg.controlFile); - } else { - printf("\r sensor error control %s loaded.\n", msg.controlFile); - } - } else { - logInfo("\rNot A Control File%s\r\n", i->c_str()); - // not a control file - } - } - } - return 0; -} - -/***************************************************************************** - * Function: createControl() - * Description: creates a new control - * - * @param none - * @return 0 on success; -1 otherwise - *****************************************************************************/ -static int createControl(const ConfigMessage_t *msg) -{ - int status = 0; - - logInfo("\r%s invoked\n", __func__); - - switch (msg->control) { - case CONTROL_SETPOINT: { - SetpointControl *setpointControl = new SetpointControl; - bool rc = setpointControl->load(msg->controlFile); - if ( rc != true ) { - logError("%s: failed to load %s\n", __func__, msg->controlFile); - delete setpointControl; - GLOBAL_mdot->deleteUserFile(msg->controlFile); - status = -1; - } else { - setpoint_mutex.lock(); - setpointTable[msg->controlFile] = setpointControl; - setpoint_mutex.unlock(); - setpointControl->start(); - } - break; - } - case CONTROL_TIMER: { - TimerControl *timerControl = new TimerControl; - bool rc = timerControl->load(msg->controlFile); - if ( rc != true ) { - logError("%s: failed to load %s\n", __func__, msg->controlFile); - delete timerControl; - GLOBAL_mdot->deleteUserFile(msg->controlFile); - status = -1; - } else { - timer_mutex.lock(); - timerTable[msg->controlFile] = timerControl; - timer_mutex.unlock(); - timerControl->start(); - } - break; - } - case CONTROL_MANUAL: { - ManualControl *manualControl = new ManualControl; - bool rc = manualControl->load(msg->controlFile); - if ( rc != true ) { - logError("%s: failed to load %s\n", __func__, msg->controlFile); - delete manualControl; - GLOBAL_mdot->deleteUserFile(msg->controlFile); - status = -1; - - } else { - manual_mutex.lock(); - manualTable[msg->controlFile] = manualControl; - manual_mutex.unlock(); - manualControl-> start(); - } - break; - } - - case CONTROL_PID: { - // TODO: PID - break; - } - case CONTROL_COMPOSITE: { - CompositeControl *compositeControl = new CompositeControl; - bool rc = compositeControl->load(msg->controlFile); - if ( rc != true ) { - logError("%s: failed to load %s\n", __func__, msg->controlFile); - delete compositeControl; - GLOBAL_mdot->deleteUserFile(msg->controlFile); - status = -1; - } else { - composite_mutex.lock(); - compositeTable[msg->controlFile] = compositeControl; - composite_mutex.unlock(); - compositeControl->start(); - } - break; - } - - case CONTROL_SEQUENCE: { - SequenceControl *sequenceControl = new SequenceControl; - bool rc = sequenceControl->load(msg->controlFile); - if ( rc != true ) { - logError("%s: failed to load %s", __func__, msg->controlFile); - delete sequenceControl; - //GLOBAL_mdot->deleteUserFile(msg->controlFile); - status = -1; - } else { - sequence_mutex.lock(); - sequenceTable[msg->controlFile] = sequenceControl; - sequence_mutex.unlock(); - sequenceControl->start(); - } - break; - } - case CONTROL_FAILSAFE: { - FailsafeControl *failsafeControl = new FailsafeControl; - bool rc = failsafeControl->load(msg->controlFile); - if ( rc != true ) { - logError("%s: failed to load %s\n", __func__, msg->controlFile); - delete failsafeControl; - GLOBAL_mdot->deleteUserFile(msg->controlFile); - status = -1; - } else { - failsafe_mutex.lock(); - failsafeTable[msg->controlFile] = failsafeControl; - failsafe_mutex.unlock(); - failsafeControl->start(); - } - break; - } - case CONTROL_SENSOR_ERROR: { - SensorErrorControl *sensorErrorControl = new SensorErrorControl; - bool rc = sensorErrorControl->load(msg->controlFile); - if ( rc != true ) { - logError("%s: failed to load %s\n", __func__, msg->controlFile); - delete sensorErrorControl; - GLOBAL_mdot->deleteUserFile(msg->controlFile); - status = -1; - } else { - sensorError_mutex.lock(); - sensorErrorTable[msg->controlFile] = sensorErrorControl; - sensorError_mutex.unlock(); - sensorErrorControl->start(); - } - break; - } - case CONTROL_ALGORITHM: { - CompositeAlgorithm *compositeAlgorithm = new CompositeAlgorithm; - bool rc = compositeAlgorithm->load(msg->controlFile); - if ( rc != true ) { - logError("%s: failed to load %s\n", __func__, msg->controlFile); - status = -1; - } else { - // add this algorithm to the table - algorithmTable[compositeAlgorithm->getId()] = compositeAlgorithm; - } - break; - } - default: - logInfo("\r%s: control type %d not implemented yet...\n", - __func__, msg->control); - break; - } - return status; -} - -/***************************************************************************** - * Function: modifyControl() - * Description: modifies a control - * - * @param msg - * @return none - *****************************************************************************/ -static int modifyControl(const ConfigMessage_t *msg) -{ - logInfo("\r%s invoked\n", __func__); - - switch (msg->control) { - case CONTROL_SETPOINT: { - // find the control in the table - StringSetpointMap::const_iterator pos; - setpoint_mutex.lock(); - pos = setpointTable.find(msg->controlFile); - if ( pos != setpointTable.end() ) { - bool rc = pos->second->load(msg->controlFile); - if ( rc != true ) { - logError("\rFailed to reload the setpoint control %s\n", msg->controlFile); - } else { - logInfo("\rReloaded the setpoint control %s\n", msg->controlFile); - } - } - setpoint_mutex.unlock(); - break; - } - case CONTROL_MANUAL: { - // find the manual control in the table - StringManualMap::const_iterator pos; - manual_mutex.lock(); - pos = manualTable.find(msg->controlFile); - if ( pos != manualTable.end() ) { - bool rc = pos->second->load(msg->controlFile); - if ( rc != true ) { - logError("\rFailed to reload the manual control %s\n", msg->controlFile); - } else { - logInfo("\rReloaded the manual control %s\n", msg->controlFile); - } - } - manual_mutex.unlock(); - break; - } - default: - logError("%s: unknown control %d\n", __func__, msg->control); - break; - } - - return 0; -} - -/***************************************************************************** - * Function: destroyControl() - * Description: destroys a controls - * - * @param msg - * @return none - *****************************************************************************/ -static int destroyControl(const ConfigMessage_t *msg) -{ - logInfo("\r%s invoked\n", __func__); - - switch ( msg->control ) { - case CONTROL_SETPOINT: { - StringSetpointMap::iterator pos; - setpoint_mutex.lock(); - pos = setpointTable.find(msg->controlFile); - if ( pos != setpointTable.end() ) { - if ( !Util_isSequenceSubControl(msg->controlFile) ) { - GLOBAL_mdot->deleteUserFile(msg->controlFile); - } - logInfo("%s: deleted %s", __func__, msg->controlFile); - pos->second->unregisterControl(); - delete (pos->second); - setpointTable.erase(pos); - } else { - logError("%s: unable to find %s\n", __func__, msg->controlFile); - } - setpoint_mutex.unlock(); - break; - } - case CONTROL_TIMER: { - StringTimerMap::iterator pos; - timer_mutex.lock(); - pos = timerTable.find(msg->controlFile); - if ( pos != timerTable.end() ) { - if ( !Util_isSequenceSubControl(msg->controlFile) ) { - GLOBAL_mdot->deleteUserFile(msg->controlFile); - } - logInfo("%s: deleted %s", __func__, msg->controlFile); - pos->second->unregisterControl(); - delete (pos->second); - timerTable.erase(pos); - } else { - logError("%s: unable to find %s\n", __func__, msg->controlFile); - } - timer_mutex.unlock(); - break; - } - case CONTROL_MANUAL: { - StringManualMap::iterator pos; - manual_mutex.lock(); - pos = manualTable.find(msg->controlFile); - if ( pos != manualTable.end() ) { - GLOBAL_mdot->deleteUserFile(msg->controlFile); - logInfo("%s: deleted %s", __func__, msg->controlFile); - // unregister with the output thread - pos->second->unregisterControl(); - delete (pos->second); - manualTable.erase(pos); - } else { - logError("%s: unable to find %s", __func__, msg->controlFile); - } - manual_mutex.unlock(); - break; - } - case CONTROL_COMPOSITE: { - StringCompositeMap::iterator pos; - composite_mutex.lock(); - pos = compositeTable.find(msg->controlFile); - if ( pos != compositeTable.end() ) { - GLOBAL_mdot->deleteUserFile(msg->controlFile); - logInfo("%s: deleted %s", __func__, msg->controlFile); - pos->second->unregisterControls(); - delete (pos->second); - compositeTable.erase(pos); - } else { - logError("%s: unable to find %s", __func__, msg->controlFile); - } - composite_mutex.unlock(); - break; - } - case CONTROL_SEQUENCE: { - StringSequenceMap::iterator pos; - sequence_mutex.lock(); - pos = sequenceTable.find(msg->controlFile); - if ( pos != sequenceTable.end() ) { - GLOBAL_mdot->deleteUserFile(msg->controlFile); - logInfo("%s: delete %s", __func__, msg->controlFile); - // TODO: - // pos->second->unregisterControls(); - delete (pos->second); - sequenceTable.erase(pos); - } else { - logError("%s: unable to find %s", __func__, msg->controlFile); - } - sequence_mutex.unlock(); - break; - } - case CONTROL_SENSOR_ERROR: { - StringSensorErrorMap::iterator pos; - sensorError_mutex.lock(); - pos = sensorErrorTable.find(msg->controlFile); - if ( pos != sensorErrorTable.end() ) { - GLOBAL_mdot->deleteUserFile(msg->controlFile); - logInfo("%s: deleted %s", __func__, msg->controlFile); - pos->second->unregisterControl(); - delete (pos->second); - sensorErrorTable.erase(pos); - } else { - logError("%s: unable to find %s", __func__, msg->controlFile); - } - sensorError_mutex.unlock(); - break; - } - case CONTROL_PID: { - // TODO: - break; - } - default: - break; - } - return 0; -} -// -// function: getManualControlType -// description: extract the manual control type (continuous or timed) -// -// @param[in] filename -// @return control type (int) -// -static unsigned int getManualControlType(const char *filename) -{ - char buf[MAX_FILE_SIZE]; - unsigned int type = MANUAL_CONTROL_TYPE_NONE; - bool rc = GLOBAL_mdot->readUserFile(filename, buf, MAX_FILE_SIZE); - if ( rc != true ) { - logError("%s: failed to read %s", __func__, filename); - } else { - cJSON * root = cJSON_Parse(buf); - type = atoi(cJSON_GetObjectItem(root,"type")->valuestring); - cJSON_Delete(root); - } - return type; -}
--- a/ICE-Application/src/ConfigurationHandler/ConfigurationHandler.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ -/****************************************************************************** - * - * File: ConfigurationHandler.h - * Desciption: interface file for the ICE Configuration Handler - * - *****************************************************************************/ -#ifndef CONFIGURATIONHANDLER_H -#define CONFIGURATIONHANDLER_H - -#include "./Controls/SetpointControl.h" -#include "./Controls/TimerControl.h" -#include "./Controls/ManualControl.h" -#include "./Controls/CompositeControl.h" -#include "./Controls/PIDControl.h" -#include "./Controls/FailsafeControl.h" -#include "./Controls/SensorErrorControl.h" -#include "./Controls/SequenceControl.h" -#include "./Algorithms/CompositeAlgorithm.h" -#include <map> - -// file naming prefix conventions -#define CONTROL_SP_STR "control_sp_" -#define CONTROL_TM_STR "control_tm_" -#define CONTROL_MN_STR "control_mn_" -#define CONTROL_COMP_STR "control_comp_" -#define CONTROL_CA_STR "control_ca_" -#define CONTROL_FS_STR "control_fs_" -#define CONTROL_SE_STR "control_se_" -#define CONTROL_PID_STR "control_pid_" -#define CONTROL_ADM_STR "control_adm_" -#define CONTROL_SEQ_STR "control_seq_" - -#define VIRTUAL_OUTPUT_PREFIX "v_" - -typedef struct va_tag { - std::string tag; - std::string opr; - std::string op; -} VirtualAlgorithm; - -void ConfigurationHandler(void const *args); - -// Public APIs -void ConfigurationHandler_showControls(void); -void ConfigurationHandler_showAlgorithms(void); -void ConfigurationHandler_showTimerControls(void); -void ConfigurationHandler_showManualControls(void); -void ConfigurationHandler_showSetpointControls(void); -void ConfigurationHandler_showCompositeControls(void); -void ConfigurationHandler_showFailsafeControls(void); -void ConfigurationHandler_showSensorErrorControls(void); -void ConfigurationHandler_showSequenceControls(void); - -// map["control_sp_1.json"] : setpointControl -typedef std::map<std::string, SetpointControl*> StringSetpointMap; - -// map["control_tm_rly01.json"] : timerControl -typedef std::map<std::string, TimerControl*> StringTimerMap; - -//map["control_mn_1.json"] : manualControl -typedef std::map<std::string, ManualControl*> StringManualMap; - -// map["control_pid_1.json"] : PIDControl -//typedef std::map<std::string, PIDControl *> StringPIDMap; - -// map["control_cmp_1.json"] : compositeControl -typedef std::map<std::string, CompositeControl*> StringCompositeMap; - -// map["control_fs_rly1.json"] : failsafeControl -typedef std::map<std::string, FailsafeControl*> StringFailsafeMap; - -// map["control_se_bdcond.json"] : sensorErrorControl -typedef std::map<std::string, SensorErrorControl*> StringSensorErrorMap; - -// map["EQUAL_TO_1"].<operands stucture> -typedef std::map<std::string, CompositeAlgorithm *> StringAlgorithmMap; - -// map["control_seq_slug.json"] : sequenceControl -typedef std::map<std::string, SequenceControl *> StringSequenceMap; - -extern StringSetpointMap setpointTable; -extern StringTimerMap timerTable; -extern StringManualMap manualTable; -//extern StringPIDMap PIDTable; -extern StringCompositeMap compositeTable; -extern StringAlgorithmMap algorithmTable; -extern StringFailsafeMap failsafeTable; -extern StringSensorErrorMap sensorErrorTable; -extern StringSequenceMap sequenceTable; - -extern Mutex manual_mutex; -extern Mutex setpoint_mutex; -extern Mutex timer_mutex; -extern Mutex failsafe_mutex; -extern Mutex sensorError_mutex; -extern Mutex composite_mutex; -extern Mutex sequence_mutex; - -#endif
--- a/ICE-Application/src/ConfigurationHandler/Controls/CompositeControl.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,372 +0,0 @@ -/****************************************************************************** - * - * File: CompositeControl.cpp - * Desciption: ICE Composite Control Class implementation - * - *****************************************************************************/ -#include "CompositeControl.h" -#include "ConfigurationHandler.h" -#include "cJSON.h" -#include "ModbusMasterApi.h" -#include "global.h" -#include "ICELog.h" -#include <string> -#include <iostream> -#include <iomanip> - -#ifdef MDOT_ICE -extern mDot *GLOBAL_mdot; -#endif - -using namespace std; - - -// -// method: load -// description: load a composite control -// -// @param[in] _controlFile -> the file containing the JSON data -// @return false if an error occurs; true otherwise -// - -bool CompositeControl::load(std::string _controlFile) -{ - controlFile = _controlFile; - - - char dataBuf[MAX_FILE_SIZE]; - - // read the control data - bool rc = GLOBAL_mdot->readUserFile(controlFile.c_str(), (void *)dataBuf, sizeof(dataBuf)); - if ( rc != true ) { - logError("%s: failed to read %s", __func__, controlFile.c_str()); - // caller should destroy the object - return false; - } - - // validate control data - if ( !validateControlData(dataBuf) ) { - logError("%s: failed to validate control data", __func__); - return false; - } - - // copy control data - copyControlData(dataBuf); - - // TODO: validate the list fo outputs - - return true; -} - -// -// method: validateControlData -// description: validate JSON formatted control data -// -// @param[in] buf -> JSON formatted string -// @param[out] none -// @return true if valid; false otherwise -// - -bool CompositeControl::validateControlData(const char *buf) -{ - bool rc = true; - cJSON * root = cJSON_Parse(buf); - - if ( !cJSON_HasObjectItem(root, "id") || - !cJSON_HasObjectItem(root, "tag") || - !cJSON_HasObjectItem(root, "priority") || - !cJSON_HasObjectItem(root, "ca") || - !cJSON_HasObjectItem(root, "outputs") ) { - logError("%s: control file missing expected tags", __func__); - cJSON_Delete(root); - return false; - } - - cJSON_Delete(root); - return rc; -} - -// -// method: copyControlData -// description: copy the JSON formatted data -// -// @param[in] buf -> JSON formatted string -// @param[out] none -// @return none -// -void CompositeControl::copyControlData(const char *buf) -{ - cJSON * root = cJSON_Parse(buf); - - id = cJSON_GetObjectItem(root,"id")->valuestring; - tag = cJSON_GetObjectItem(root, "tag")->valuestring; - priority = atoi(cJSON_GetObjectItem(root, "priority")->valuestring); - ca = cJSON_GetObjectItem(root, "ca")->valuestring; - cJSON *array = cJSON_GetObjectItem(root, "outputs"); - for ( int i = 0; i < cJSON_GetArraySize(array); ++i ) { - cJSON *subitem = cJSON_GetArrayItem(array, i); - std::string tag = cJSON_GetObjectItem(subitem, "tag")->valuestring; - std::string response = cJSON_GetObjectItem(subitem, "responseA")->valuestring; - OutputElement x = { tag, response }; - outputs.push_back(x); - } - cJSON_Delete(root); -} - -// -// method: start -// description: start the composite control -// -// @param none -// @return none -// -void CompositeControl::start(void) -{ - currentState = STATE_START; -} - -// -// method: update -// description: updater for the composite control -// -// @param none -// @return none -// -CompositeControlError_t CompositeControl::update(void) -{ - CompositeControlError_t rc = COMPOSITE_CONTROL_OK; - std::string function; - - switch ( currentState ) { - case STATE_INIT: - // do nothing - break; - case STATE_START: - function = executeCommand(); - if ( function == "responseA" ) { - currentState = STATE_CONTROL_ON; - triggerOutputs(function); - } else if ( function == "nothing" ) { - currentState = STATE_CONTROL_OFF; - } - break; - case STATE_CONTROL_ON: - function = executeCommand(); - if ( function == "nothing" ) { - currentState = STATE_CONTROL_OFF; - unregisterControls(); - } else { - // do nothing - } - break; - case STATE_CONTROL_OFF: - function = executeCommand(); - if ( function == "responseA" ) { - currentState = STATE_CONTROL_ON; - triggerOutputs(function); - } else { - // do nothing - } - break; - default: - logError("%s: unknown state %d", __func__, this->currentState); - rc = COMPOSITE_CONTROL_UNK_STATE; - break; - } - return rc; -} - -// -// method: executeCommand -// description: execute the command specified in the control algorithm -// -// @param none -// @return none -// -std::string CompositeControl::executeCommand(void) -{ - // look up the algorithm - StringAlgorithmMap::const_iterator pos; - pos = algorithmTable.find(this->ca); - if ( pos != algorithmTable.end() ) { - // we found the control algorithm - return this->executeOperation(pos->second); - } - return "nothing"; -} - -// -// method: executeOperation -// description: execute an operations from the control equation -// -// @param[in] ca -> composite control algorithm -// @return string to the result -// -std::string CompositeControl::executeOperation(const CompositeAlgorithm *ca) -{ - // (this->tag) <op> <opr> = <result> - // - // example: - // this->tag = "i_flowswitch" - // opr = "1" - // op = "==" - // if true return "responseA" else return "nothing" - - ModbusValue value; - bool rc = ModbusMasterReadRegister(tag,&value); - if ( rc != true ) { - logError("%s cannot find tag", __func__); - return "nothing"; - } - - // equal to operator - if ( ca->getOp() == "==" ) { - // perform the equality operation - if ( value.value == atof(ca->getOpr().c_str()) ) { - return ca->getResultTrue(); - } else { - return ca->getResultFalse(); - } - } - if ( ca->getOp() == ">=" ) { - if ( value.value >= atof(ca->getOpr().c_str()) ) { - return ca->getResultTrue(); - } else { - return ca->getResultFalse(); - } - } - - if ( ca->getOp() == "&" ) { - if ( (int)value.value & (int)atoi(ca->getOpr().c_str()) ) { - return ca->getResultTrue(); - } else { - return ca->getResultFalse(); - } - } - - // addition operator - if ( ca->getOp() == "+" ) { - // TODO - } - // multiply operator - if ( ca->getOp() == "*" ) { - // TODO: - } - // subtraction operator - if ( ca->getOp() == "-" ) { - // TODO: - } - - return "nothing"; -} - -// -// method: triggerOutputs -// description: trigger the output(s) to do something -// -// @param[in] result -> the result of the operation -// @return none -// -void CompositeControl::triggerOutputs(std::string result) -{ - - // loop through the list - StringAlgorithmMap::const_iterator pos; - pos = algorithmTable.find(this->ca); - if ( pos != algorithmTable.end() ) { - std::vector<OutputElement>::const_iterator it; - for ( it = outputs.begin(); it != outputs.end(); ++it ) { - if ( it->response == "fixed off" ) { - printf("\rSending an OFF control for %s\n", it->tag.c_str()); - sendMail(it->tag, ACTION_CONTROL_OFF); - } else if ( it->response == "fixed on" ) { - printf("\rSending an ON request for %s\n", it->tag.c_str()); - sendMail(it->tag, ACTION_CONTROL_ON); - } - } - } else { - logError("%s: failed to find the control algorithm %s\n", __func__, this->ca.c_str()); - } -} - -// -// method: sendMail -// description: send mail to the output task -// -// @param[in] io_tag -> input/output tag -// @param[in] action -> ON, OFF, UNREGISTER -// @return none -// -void CompositeControl::sendMail(const std::string io_tag, OutputAction action) -{ - logInfo("%s: composite control attempting to send action %d\n", - __func__, action); - - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->action = action; - output_mail->controlType = CONTROL_COMPOSITE; - output_mail->priority = this->priority; - - strncpy(output_mail->input_tag, this->tag.c_str(), sizeof(output_mail->input_tag)-1); - strncpy(output_mail->output_tag, io_tag.c_str(), sizeof(output_mail->output_tag)-1); - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - - OutputMasterMailBox.put(output_mail); -} - -// -// method: unregisterControls -// description: unregister the control with the output task -// -// @param none -// @return none -// -void CompositeControl::unregisterControls(void) -{ - // loop through the list - StringAlgorithmMap::const_iterator pos; - pos = algorithmTable.find(this->ca); - if ( pos != algorithmTable.end() ) { - std::vector<OutputElement>::const_iterator it; - for ( it = outputs.begin(); it != outputs.end(); ++it ) { - sendMail(it->tag, ACTION_CONTROL_UNREGISTER); - } - } else { - logError("%s: failed to find the control algorithm %s\n", __func__, this->ca.c_str()); - } -} - -// -// method: display -// description: display the pertinents -// -// @param none -// @return none -// -void CompositeControl::display(void) -{ - const char *mapper[] = { "INIT", - "START", - "CONTROL_OFF", - "CONTROL_ON" - }; - - printf("\r\n"); - std::cout << left << setw(10) << setfill(' ') << "composite: "; - std::cout << left << setw(40) << setfill(' ') << controlFile; - std::cout << left << setw(20) << setfill(' ') << id; - std::cout << left << setw(20) << setfill(' ') << tag; - std::cout << left << setw(6) << setfill(' ') << priority; - std::cout << left << setw(20) << setfill(' ') << ca; - std::cout << left << setw(16) << setfill(' ') << mapper[currentState]; - - vector<OutputElement>::const_iterator it; - for ( it = outputs.begin(); it != outputs.end(); ++it ) { - std::cout << left << (*it).tag << ":" << (*it).response << " "; - } - - std::cout.flush(); - -} \ No newline at end of file
--- a/ICE-Application/src/ConfigurationHandler/Controls/CompositeControl.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -/****************************************************************************** - * - * File: CompositeControl.h - * Desciption: ICE Composite Control Class - * - *****************************************************************************/ -#ifndef COMPOSITECONTROL_H -#define COMPOSITECONTROL_H - -#include <string> -#include <vector> -#include <stdio.h> -#include <stdlib.h> -#include "global.h" -#include "./Algorithms/CompositeAlgorithm.h" - -typedef enum { - COMPOSITE_CONTROL_OK, - COMPOSITE_CONTROL_UNK_STATE, - COMPOSITE_CONTROL_ERROR, - COMPOSITE_CONTROL_DESTROY -} CompositeControlError_t; - -//! Composite Control - used to implement a composite control -class CompositeControl -{ -private: - typedef struct oe_tag { - std::string tag; - std::string response; - } OutputElement; - - std::string controlFile; // the control file - std::string id; // composite identifier - std::string tag; // i/o tag to evaluate - unsigned int priority; // control priority - std::string ca; // control algorithm - std::vector<OutputElement> outputs; // (virtual) output(s) - - enum State { - STATE_INIT, - STATE_START, - STATE_CONTROL_OFF, - STATE_CONTROL_ON, - STATE_MAX - }; - State currentState; // current state - - bool validateControlData(const char *buf); - void copyControlData(const char *buf); - - std::string executeCommand(void); - std::string executeOperation(const CompositeAlgorithm*); - void triggerOutputs(std::string result); - void sendMail(std::string io_tag, OutputAction action); - -public: - /// constructor - CompositeControl() { } - - /// destructor - ~CompositeControl() { - printf("\r%s invoked\n", __func__); - } - - /// load a composite control from a JSON configuration file - bool load(std::string controlFile); - - /// start a composite control instance - void start(void); - - /// update a composite control instance - CompositeControlError_t update(void); - - /// unregister a composite control(s) instance - void unregisterControls(); - - /// get the composite control's control filename - std::string getControlFile(void) const { - return controlFile; - } - - /// get the composite control's identifier - std::string getId(void) const { - return id; - } - - /// get the composite control's current state - State getState(void) const { - return currentState; - } - - /// get the composite control's tag - std::string getTag(void) const { - return tag; - } - - /// get a list of a composite control's outputs - std::vector<std::string> getOutputs(void) const; - - void display(void); -}; - -#endif
--- a/ICE-Application/src/ConfigurationHandler/Controls/FailsafeControl.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,534 +0,0 @@ -/****************************************************************************** - * - * File: FailsafeControl.cpp - * Desciption: ICE Failsafe Control Class implementation - * - *****************************************************************************/ -#include "FailsafeControl.h" -#include "cJSON.h" -#include "ICELog.h" -#include "global.h" -#include "ModbusMasterApi.h" -#include "utilities.h" -#include <string> -#include <iostream> -#include <iomanip> -#include <stdarg.h> -#include <stdlib.h> -#include <time.h> - -using namespace std; - -#ifdef MDOT_ICE -extern mDot *GLOBAL_mdot; -#endif - -// for debugging - this can be set via the console command: -// "debug-fs 1 or debug-fs 0 -bool debugFailsafeControl = false; - -static void debug(const char *fmt, ...) -{ - if ( debugFailsafeControl ) { - va_list vargs; - va_start(vargs, fmt); - vfprintf(stdout, fmt, vargs); - va_end(vargs); - } -} - -// -// method: load -// description: load a composite control -// -// @param none -// @return none -// -bool FailsafeControl::load(std::string _controlFile) -{ - controlFile = _controlFile; - - - // read the data into a buffer - char dataBuf[MAX_FILE_SIZE]; - - bool rc = GLOBAL_mdot->readUserFile(controlFile.c_str(), (void *)dataBuf, sizeof(dataBuf)); - if ( rc != true ) { - logError("%s: failed to read %s", __func__, controlFile.c_str()); - // caller should destroy the object - return false; - } - - - if ( !validateControlData(dataBuf) ) { - logError("%s: failed to validate control data", __func__); - return false; - } - - copyControlData(dataBuf); - - ModbusValue val; - // validate the input & output - if ( ModbusMasterReadRegister(input, &val) == false ) { - logError("%s failed to find input %s", id.c_str(), input.c_str()); - return false; - } - if ( ModbusMasterReadRegister(output, &val) == false ) { - logError("%s failed to find output %s", id.c_str(), output.c_str()); - return false; - } - - isVirtualOutput = Util_isVirtualOutput(output) ? true : false; - return true; -} -// -// method: validateControlData -// description: validate the JSON formatted strings for -// expected tags -// -// @param[in] buf -> JSON formatted string -// @param[out] none -// @return true if valid; false otherwise -// -bool FailsafeControl::validateControlData(const char *buf) -{ - bool rc = true; - // parse the data - cJSON * root = cJSON_Parse(buf); - - if ( !cJSON_HasObjectItem(root, "id") || - !cJSON_HasObjectItem(root, "priority") || - !cJSON_HasObjectItem(root, "input") || - !cJSON_HasObjectItem(root, "output") || - !cJSON_HasObjectItem(root, "hfsValue") || - !cJSON_HasObjectItem(root, "hfsDutyCycle") || - !cJSON_HasObjectItem(root, "hfsInterval") || - !cJSON_HasObjectItem(root, "lfsValue") || - !cJSON_HasObjectItem(root, "lfsDutyCycle") || - !cJSON_HasObjectItem(root, "lfsInterval") ) { - logError("%s: control file is missing expected tags", __func__); - rc = false; - } - - cJSON_Delete(root); - return rc; -} - -// -// method: copyControlData -// description: copy data from JSON buffer to object -// -// @param[in] buf -> JSON formattted string -// @param[out] none -// @return none -// -void FailsafeControl::copyControlData(const char *buf) -{ - cJSON *root = cJSON_Parse(buf); - - id = cJSON_GetObjectItem(root, "id")->valuestring; - priority = atoi(cJSON_GetObjectItem(root, "priority")->valuestring); - input = cJSON_GetObjectItem(root, "input")->valuestring; - output = cJSON_GetObjectItem(root, "output")->valuestring; - - // high failsafe data - hfs_data.value = atof(cJSON_GetObjectItem(root, "hfsValue")->valuestring); - hfs_data.dutyCycle = atoi(cJSON_GetObjectItem(root, "hfsDutyCycle")->valuestring); - hfs_data.interval = atoi(cJSON_GetObjectItem(root, "hfsInterval")->valuestring); - - // low failsafe data - lfs_data.value = atof(cJSON_GetObjectItem(root, "lfsValue")->valuestring); - lfs_data.dutyCycle = atoi(cJSON_GetObjectItem(root, "lfsDutyCycle")->valuestring); - lfs_data.interval = atoi(cJSON_GetObjectItem(root, "lfsInterval")->valuestring); - - cJSON_Delete(root); -} - -// -// method: start -// description: start the failsafe control -// -// @param none -// @return none -// -void FailsafeControl::start(void) -{ - currentState = STATE_START; -} - -// -// method: update -// description: update the faisafe control using the FSM -// -// @param none -// @return none -// -FailsafeControlError_t FailsafeControl::update(void) -{ - FailsafeControlError_t rc = FAILSAFE_CONTROL_OK; - - switch (this->currentState) { - case STATE_INIT: - // do nothing - control must be programatically started - break; - case STATE_START: - // control has programmatically started, do some checking - if ( this->aboveHighFailsafe() ) { - if ( hfs_data.dutyCycle) { - debug("\r%s: [START]->above hfs->[ON_HFS]\n", id.c_str()); - // start high failsafe duty cycle - this->currentState = STATE_CONTROL_ON_HFS; - // turn the pump ON for the duty cycle, start the timer - sendMailToOutput(ACTION_CONTROL_ON); - this->startHfsDutyTimer(); - } else { - // no duty cycle specified, so go into the high-faisafe - // fixed-off state - debug("\r%s: [START]->above hfs && !dc->[OFF_HFS]\n", id.c_str()); - this->currentState = STATE_CONTROL_OFF_HFS; - sendMailToOutput(ACTION_CONTROL_OFF); - // no need to start the duty timer - } - } else if ( this->belowLowFailsafe() ) { - debug("\r%s: [START]->below lfs->[ON_LFS]\n", id.c_str()); - // start low failsafe duty cycle - if ( lfs_data.dutyCycle ) { - this->currentState = STATE_CONTROL_ON_LFS; - // turn the pump ON for the duty cycle, start the timer - sendMailToOutput(ACTION_CONTROL_ON); - this->startLfsDutyTimer(); - } else { - // no duty cycle specified, so go into the low-failsafe - // fixed off-state - this->currentState = STATE_CONTROL_OFF_LFS; - sendMailToOutput(ACTION_CONTROL_OFF); - // no need to start the timer - } - } else { - debug("\r%s: [START]->no conditions->[OFF]\n", id.c_str()); - this->currentState = STATE_CONTROL_OFF; - } - break; - case STATE_CONTROL_OFF: - // control is acting normal, within bounds - if ( this->aboveHighFailsafe() ) { - if ( hfs_data.dutyCycle ) { - debug("\r%s: [OFF]->above hfs->[ON HFS]\n", id.c_str()); - this->currentState = STATE_CONTROL_ON_HFS; - sendMailToOutput(ACTION_CONTROL_ON); - this->startHfsDutyTimer(); - } else { - debug("\r%s: [OFF]->above hfs && no dc->[OFF HFS]\n", id.c_str()); - this->currentState = STATE_CONTROL_OFF_HFS; - sendMailToOutput(ACTION_CONTROL_OFF); - } - } else if ( this->belowLowFailsafe() ) { - // restart the low failsafe duty cycle - debug("\r%s: [OFF]->below lfs->[ON_LFS]\n", id.c_str()); - if ( lfs_data.dutyCycle ) { - this->currentState = STATE_CONTROL_ON_LFS; - sendMailToOutput(ACTION_CONTROL_ON); - this->startLfsDutyTimer(); - } else { - this->currentState = STATE_CONTROL_OFF_LFS; - sendMailToOutput(ACTION_CONTROL_OFF); - } - } else { - // do nothing - } - break; - case STATE_CONTROL_ON_LFS: - // control is in low-failsafe with duty cycle ON - if ( !this->belowLowFailsafe() ) { - // input has fallen below the failsafe, so turn off the control - debug("\r%s: [ON_LFS]->above lfs->[OFF]\n", id.c_str()); - this->currentState = STATE_CONTROL_OFF; - this->unregisterControl(); - } else if ( this->dutyOnExpired() ) { - // duty ON has expired, so let's idle in the OFF-LFS state now - debug("\r%s: [ON_LFS]->duty on expired->[OFF LFS]\n", id.c_str()); - this->currentState = STATE_CONTROL_OFF_LFS; - sendMailToOutput(ACTION_CONTROL_OFF); - } else { - // do nothing - } - break; - case STATE_CONTROL_OFF_LFS: - // control is in low-failsafe with duty cycle OFF - if ( !this->belowLowFailsafe() ) { - // no longer in dangerous waters, so transition to OFF - debug("\r%s: [OFF_LFS]->above lfs->[OFF]\n", id.c_str()); - this->currentState = STATE_CONTROL_OFF; - this->unregisterControl(); - } else if ( this->dutyOffExpired() && lfs_data.dutyCycle ) { - debug("\r%s: [OFF_LFS]->duty OFF exp->[ON_LFS]", id.c_str()); - // turn the PUMP back on and restart the duty timer - this->currentState = STATE_CONTROL_ON_LFS; - sendMailToOutput(ACTION_CONTROL_ON); - this->startLfsDutyTimer(); - - } else { - // do nothing - } - break; - case STATE_CONTROL_ON_HFS: - // control is in high-failsafe with duty cycle ON - if ( !this->aboveHighFailsafe() ) { - debug("\r%s: [ON_HFS]->below hfs->[OFF]\n", id.c_str()); - this->currentState = STATE_CONTROL_OFF; - this->unregisterControl(); - } else if ( this->dutyOnExpired() ) { - debug("\r%s: [ON_HFS]->duty ON expired->[OFF_HFS]\n", id.c_str()); - this->currentState = STATE_CONTROL_OFF_HFS; - sendMailToOutput(ACTION_CONTROL_OFF); - } else { - // do nothing - } - break; - case STATE_CONTROL_OFF_HFS: - // control is in high-failsafe with cuty cycle OFF - if ( !this->aboveHighFailsafe() ) { - debug("\r%s: [OFF_HFS]->below hfs->[OFF]\n", id.c_str()); - this->currentState = STATE_CONTROL_OFF; - this->unregisterControl(); - } else if ( this->dutyOffExpired() && hfs_data.dutyCycle ) { - debug("\r%s: [OFF_HFS]->duty OFF expired->[ON_HFS]\n", id.c_str()); - this->currentState = STATE_CONTROL_ON_HFS; - sendMailToOutput(ACTION_CONTROL_ON); - this->startHfsDutyTimer(); - } else { - // do nothing - } - break; - default: - logError("%s: unknown state %d", __func__, this->currentState); - rc = FAILSAFE_CONTROL_UNK_STATE; - break; - } - return rc; -} - -// -// method: belowFailsafe -// description: returns true if the input signal is below the -// failsafe mark -// -// @param[in] none -// @param[out] none -// @return true if below; false otherwise -// -bool FailsafeControl::belowLowFailsafe(void) -{ - // read the modbus input - ModbusValue value; - ModbusMasterReadRegister(input, &value); - return ( value.value <= lfs_data.value ); -} - -// -// method: aboveHighFailsafe -// description: returns true if the input signal is above the -// failsafe mark -// -// @param[in] none -// @param[out] none -// @return true if above; false otherwise -// -bool FailsafeControl::aboveHighFailsafe(void) -{ - // read the modbus input - ModbusValue value; - ModbusMasterReadRegister(input, &value); - return ( value.value >= hfs_data.value ); -} - -// -// method: startHfsDutyTimer -// description: start the high failsafe duty timer -// -// @param none -// @return none -// -void FailsafeControl::startHfsDutyTimer(void) -{ - unsigned long currentTime = time(NULL); - unsigned long period = hfs_data.interval * 60; - - duty_timer.offTime = currentTime + ((double)period * ((double)hfs_data.dutyCycle/100.0)); - duty_timer.expirationTime = currentTime + period; - - debug("\r%s:%s-> currentTime = %lu\n", __func__, id.c_str(), currentTime); - debug("\r%s:%s-> off Time = %lu\n", __func__, id.c_str(), duty_timer.offTime); - debug("\r%s:%s-> expiration = %lu\n", __func__, id.c_str(),duty_timer.expirationTime); -} - -// -// method: stopHfsDutyTimer -// description: stop the high failsafe duty timer -// -// @param [in] none -// @param[out] none -// @return none -// -void FailsafeControl::stopHfsDutyTimer(void) -{ - debug("\r%sL%s-> invoked\n", __func__, id.c_str()); - memset(&duty_timer, 0, sizeof(duty_timer)); -} - -// -// method: startLfsDutyTimer -// descrption: start the low failsafe duty-timer -// -// @param[in] none -// @param[out] none -// @return none -// -void FailsafeControl::startLfsDutyTimer(void) -{ - unsigned long currentTime = time(NULL); - unsigned long period = lfs_data.interval * 60; - - duty_timer.offTime = currentTime + ((double)period * ((double)lfs_data.dutyCycle/100.0)); - duty_timer.expirationTime = currentTime + period; - - debug("\r%s:%s-> next off time = %lu\n", __func__, id.c_str(), duty_timer.offTime); - debug("\r%s:%s->expiration time = %lu\n", __func__, id.c_str(), duty_timer.expirationTime); -} - -// -// method: stopLfsDutyTimer -// description: stop the low failsafe duty-timer -// -// @param none -// @return none -// -void FailsafeControl::stopLfsDutyTimer(void) -{ - debug("\r%s:5s-> invoked\n", __func__, id.c_str()); - memset(&duty_timer, 0, sizeof(duty_timer)); -} - -// -// method: dutyOnExpired -// description: returns true if ON cycle has expired; false otherwise -// -// @param none -// @return none -// -bool FailsafeControl::dutyOnExpired(void) -{ - return (time(0) >= duty_timer.offTime); -} - -// -// method: dutyOffExpired -// description: returns true if OFF cycle has expired; false otherwise -// -// @param none -// @return none -// -bool FailsafeControl::dutyOffExpired(void) -{ - return (duty_timer.expirationTime < time(NULL)); -} - -// -// method: sendMailToOutput -// description: send mail to the output task -// -// @param io_tag -> input/output tag -// @param action -> ON, OFF, UNREGISTER -// @return none -// -void FailsafeControl::sendMailToOutput(OutputAction action) -{ - logInfo("%s: failsafe control %s attempting to send action %d\n", - __func__, id.c_str(), action); - - if ( isVirtualOutput ) { - debug("%s:%s-> updating the virtual output %s\n", __func__, id.c_str(), output.c_str()); - ModbusMasterWriteRegister(this->output, - (action == ACTION_CONTROL_ON) ? 1.0 : 0.0); - } else { - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->action = action; - output_mail->controlType = CONTROL_FAILSAFE; - output_mail->priority = this->priority; - - strncpy(output_mail->input_tag, this->input.c_str(), sizeof(output_mail->input_tag)-1); - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - - OutputMasterMailBox.put(output_mail); - } -} - - -// -// method: unregisterControl -// description: unregister this control with the output task -// -// @param none -// @return none -// -void FailsafeControl::unregisterControl(void) -{ - logInfo("%s: %s attempting to unregister %s\n", - __func__, id.c_str(), controlFile.c_str()); - - if ( isVirtualOutput ) { - debug("%s: %s attempting to unregister virtual output %s\n", - __func__, id.c_str(), output.c_str()); - ModbusMasterWriteRegister(output, 0.0); - } else { - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->action = ACTION_CONTROL_UNREGISTER; - output_mail->controlType = CONTROL_FAILSAFE; - output_mail->priority = this->priority; - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - - OutputMasterMailBox.put(output_mail); - } -} - -// -// method: display -// description: display the pertinents -// -// @param none -// @return none -// -void FailsafeControl::display(void) -{ - const char *mapper[] = { "INIT", - "START", - "OFF", - "ON_LFS", - "OFF_LFS", - "ON_HFS", - "OFF_HFS", - "SENSOR_ERR", - "invalid" - }; - - printf("\r\n"); - std::cout << left << setw(10) << setfill(' ') << "failsafe: "; - std::cout << left << setw(40) << setfill(' ') << controlFile; - std::cout << left << setw(20) << setfill(' ') << id; - std::cout << left << setw(6) << setfill(' ') << priority; - std::cout << left << setw(20) << setfill(' ') << input; - std::cout << left << setw(20) << setfill(' ') << output; - std::cout << left << setw(16) << setfill(' ') << mapper[currentState]; - std::cout << left << setw(12) << setfill(' ') << "lfs-> " - << lfs_data.value << ":" << lfs_data.dutyCycle << ":" << lfs_data.interval; - std::cout << right << setw(12) << setfill(' ') << "hfs-> " - << hfs_data.value << ":" << hfs_data.dutyCycle << ":" << hfs_data.interval; - - std::cout.flush(); -} \ No newline at end of file
--- a/ICE-Application/src/ConfigurationHandler/Controls/FailsafeControl.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -#ifndef FAILSAFECONTROL_H -#define FAILSAFECONTROL_H - -#include <stdio.h> -#include <string> -#include "global.h" - -#define FAILSAFE_CONTROL_DEBUG - -typedef enum { - FAILSAFE_CONTROL_OK, - FAILSAFE_CONTROL_UNK_STATE, - FAILSAFE_CONTROL_ERROR, -} FailsafeControlError_t; - -//! FailsafeControl - implements a failsafe by reading values from an -// input signal and manipulating an output. -class FailsafeControl -{ -private: - std::string controlFile; // the control file - std::string id; // unique control identifier - std::string input; // sensor to read - std::string output; // output to manipulate - unsigned int priority; // control priority (typically 700) - - typedef struct hfs_tag { - float value; // high-failsafe trigger value - float dutyCycle; // percentage of on-time - float interval; // in minutes - } hfs; - typedef struct lfs_tag { - float value; // low-failsafe trigger value - float dutyCycle; // percentag of on-time - float interval; // in minutes - } lfs; - - lfs lfs_data; // Low Failsafe data - hfs hfs_data; // High Failsafe data - - typedef struct timer_tag { - unsigned long offTime; // epoch time - unsigned long expirationTime; // epoch time - } Timer_t; - - Timer_t duty_timer; // the duty timer - - enum State { - STATE_INIT, // object instantiated - STATE_START, // control has been started - STATE_CONTROL_OFF, // control is not above/below limit - STATE_CONTROL_ON_LFS, // low-failsafe with output on - STATE_CONTROL_OFF_LFS, // low-failsafe with output off - STATE_CONTROL_ON_HFS, // high-failsafe with output on - STATE_CONTROL_OFF_HFS, // high-failsafe with output off - STATE_CONTROL_SENSOR_ERROR, // input sensor is faulted - STATE_MAX - }; - State currentState; - - bool isVirtualOutput; - - bool validateControlData(const char *buf); - void copyControlData(const char *buf); - - bool aboveHighFailsafe(void); // boolean check if above HFS watermark - bool belowLowFailsafe(void); // boolean check if below LFS watermark - - void startHfsDutyTimer(void); // start the HFS duty timer - void stopHfsDutyTimer(void); // stop the HFS duty timer - - void startLfsDutyTimer(void); // start the LFS duty timer - void stopLfsDutyTimer(void); // stop the LFS duty timer - - bool dutyOnExpired(void); // boolean check if duty ON-time expired - bool dutyOffExpired(void); // boolean check if duty OFF-time expired - - bool sensorError(void); // boolean check if input is faulted - - void sendMailToOutput(OutputAction action); - -public: - /// Create a failsafe control instance - FailsafeControl() { - currentState = STATE_INIT; - isVirtualOutput = false; - } - /// Destroy a failsafe control instance - ~FailsafeControl() { - // "erased...from existence!" -- Doc Brown - printf("\r%s invoked\n", __func__); - } - - /// load a failsafe control instance with data from a JSON configuration file - bool load(std::string filename); - - /// start a failsafe control instance - void start(void); - - /// update a failsafe control instance - FailsafeControlError_t update(void); - - /// unregister a failsafe control instance - void unregisterControl(void); - - - /// display the pertinent data of a failsafe control instance - void display(void); - - /// get control identifier - std::string getId() const { - return id; - } -}; - -#endif \ No newline at end of file
--- a/ICE-Application/src/ConfigurationHandler/Controls/ManualControl.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,295 +0,0 @@ -/****************************************************************************** - * - * File: ManualControl.cpp - * Desciption: ICE Manual Control Class implementation - * - *****************************************************************************/ -#include "ManualControl.h" -#include "ICELog.h" -#include "cJSON.h" -#include "ModbusMasterApi.h" -#include "global.h" -#include <string> -#include <iostream> -#include <iomanip> -#include <stdarg.h> -#include <stdlib.h> -#include <time.h> - -using namespace std; - -// for debugging - this can be set via the console command: -// "debug-man 1 or debug-man 0 -bool debugManualControl = false; - -static void debug(const char *fmt, ...) -{ - if ( debugManualControl ) { - va_list vargs; - va_start(vargs, fmt); - vfprintf(stdout, fmt, vargs); - va_end(vargs); - } -} - -#ifdef MDOT_ICE -extern mDot *GLOBAL_mdot; -#endif - -// -// method: load -// description: open the configuration file and assign data to the -// setpoint control object -// -// @param controlFile -> name of the control file -// @return true if data is assigned; false on error -// -bool ManualControl::load(std::string _controlFile) -{ - controlFile = _controlFile; - endTime = 0; - - char dataBuf[MAX_FILE_SIZE]; - - // read the data into the buffer - bool rc = GLOBAL_mdot->readUserFile(controlFile.c_str(), (void *)dataBuf, sizeof(dataBuf)); - if ( rc != true ) { - logError("%s: failed to read %s", __func__, controlFile.c_str()); - // caller should destroy the object - return false; - } - - // validate the JSON tags in the control file - if ( validateControlData(dataBuf) != true ) { - logError("%s: invalid control data.", __func__); - return false; - } - - // assign object data from control file - copyControlData(dataBuf); - - ModbusValue val; - // validate the output - if ( ModbusMasterReadRegister(output, &val) == false ) { - logError("%s failed to find output %s", id.c_str(), output.c_str()); - return false; - } - - return true; -} - -// -// method: validateControlData -// description: validate JSON formatted data -// -// @param[in] buf -> JSON formatted string -// @param[out] none -// @return true if valid; false otherwise -// -bool ManualControl::validateControlData(const char* buf) -{ - bool rc = true; - cJSON * root = cJSON_Parse(buf); - - if ( !cJSON_HasObjectItem(root, "id") || - !cJSON_HasObjectItem(root, "output") || - !cJSON_HasObjectItem(root, "type") || - !cJSON_HasObjectItem(root, "priority") || - !cJSON_HasObjectItem(root, "duration") || - !cJSON_HasObjectItem(root, "setpoint") || - !cJSON_HasObjectItem(root, "state") || - !cJSON_HasObjectItem(root, "percent") ) { - logError("%s: control file is missing expected tags", __func__); - rc = false; - } - cJSON_Delete(root); - return rc; -} - -// -// method: copyControlData -// description: copy JSON formatted control data to object -// -// @param[in] buf -> JSON formatted string -// @param[out] none -// @return none -// -void ManualControl::copyControlData(const char *buf) -{ - cJSON * root = cJSON_Parse(buf); - - id = cJSON_GetObjectItem(root,"id")->valuestring; - output = cJSON_GetObjectItem(root,"output")->valuestring; - type = atoi(cJSON_GetObjectItem(root,"type")->valuestring); - priority = atoi(cJSON_GetObjectItem(root, "priority")->valuestring); - duration = atoi(cJSON_GetObjectItem(root, "duration")->valuestring); - setpoint = atof(cJSON_GetObjectItem(root, "setpoint")->valuestring); - state = atoi(cJSON_GetObjectItem(root, "state")->valuestring); - percent = atoi(cJSON_GetObjectItem(root, "percent")->valuestring); - - cJSON_Delete(root); -} - -// -// method: start -// description: start the manual control -// -// @param none -// @return none -// -void ManualControl::start(void) -{ - currentState = STATE_STARTUP; - if ( type == MANUAL_CONTROL_TYPE_TIMED ) { - debug("\r%s:%s-> manual control is timed\n", __func__, id.c_str()); - endTime = time(NULL) + duration; - } -} - -// -// method: update -// description: the manual control's state machine -// -// @param none -// @return none -// -ManualControlError_t ManualControl::update(void) -{ - ManualControlError_t rc = MANUAL_CONTROL_OK; - - switch ( this->currentState ) { - case STATE_INIT: - // do nothing - break; - case STATE_STARTUP: - if ( state & 0x01 ) { - this->currentState = STATE_CONTROL_ON; - debug("\r%s: [STARTUP]->manual on->[ON]\n", id.c_str()); - } else { - this->currentState = STATE_CONTROL_OFF; - debug("\r%s: [STARTUP]->manual off->[OFF]\n", id.c_str()); - - } - //this->currentState = (state & 0x01) ? STATE_CONTROL_ON : STATE_CONTROL_OFF; - this->powerOutput(); - break; - case STATE_CONTROL_ON: - if ( !(state & 0x1) ) { - this->currentState = STATE_CONTROL_OFF; - // user has changed state, so this is (or becomes) a pure - // manual control - endTime = 0; - this->powerOutput(); - debug("\r%s: [ON]->manual off->[OFF]\n", id.c_str()); - } else if ( endTime != 0 && time(NULL) >= endTime ) { - // timed interval has elapsed - currentState = STATE_CONTROL_FINISHED; - debug("\r%s: [ON]->timer expired->[FINISHED]\n", id.c_str()); - } - break; - case STATE_CONTROL_OFF: - if ( state & 0x1 ) { - this->currentState = STATE_CONTROL_ON; - // user has changed the state, so this (or becomes) a pure - // manual control - endTime = 0; - this->powerOutput(); - debug("\r%s: [OFF]->manual on->[ON]\n", id.c_str()); - } else if ( endTime != 0 && time(NULL) >= endTime ) { - // timed interval has elapsed - currentState = STATE_CONTROL_FINISHED; - debug("\r%s: [OFF]->timer expired->[FINISHED]\n", id.c_str()); - } - break; - case STATE_CONTROL_FINISHED: - // mark for deletion (control task will do the cleanup) - rc = MANUAL_CONTROL_DESTROY; - break; - default: - logError("%s unknown state %d\n", __func__, this->currentState); - rc = MANUAL_CONTROL_ERROR; - break; - } - return rc; -} - -// -// method: unregisterControl -// description: unregister this control with the output master -// -// @param none -// @return none -// -int ManualControl::unregisterControl(void) -{ - logInfo("%s: Attempting to unregister %s\n", - __func__, controlFile.c_str()); - - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->controlType = CONTROL_MANUAL; - output_mail->action = ACTION_CONTROL_UNREGISTER; - output_mail->priority = this->priority; - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - - OutputMasterMailBox.put(output_mail); - return 0; -} - -// -// method: powerOutput -// description: send a message to the output thread to power the relay -// -// @param none -// @return -1 on error; 0 otherwise -// -int ManualControl::powerOutput(void) -{ - printf("\r%s-> id:%s attempting to manually turn %s relay %s\n", - __func__, id.c_str(), (state & 0x1) ? "ON" : "OFF", output.c_str()); - - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->controlType = CONTROL_MANUAL; - output_mail->action = (state & 0x1) ? ACTION_CONTROL_ON : ACTION_CONTROL_OFF; - output_mail->priority = this->priority; - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - - OutputMasterMailBox.put(output_mail); - return 0; -} - -// -// method: display -// description: display the pertinents -// -// @param none -// @return none -// -void ManualControl::display(void) -{ - string mapper[] = { "INIT", - "STARTUP", - "CONTROL_ON", - "CONTROL_OFF" - }; - - printf("\r\n"); - std::cout << left << setw(10) << setfill(' ') << "manual: "; - std::cout << left << setw(40) << setfill(' ') << controlFile; - std::cout << left << setw(20) << setfill(' ') << id; - std::cout << left << setw(20) << setfill(' ') << output; - std::cout << left << setw(6) << setfill(' ') << type; - std::cout << left << setw(6) << setfill(' ') << priority; - std::cout << left << setw(8) << setfill(' ') << duration; - //std::cout << left << setw(8) << setfill(' ') << percent; - std::cout << left << setw(16) << setfill(' ') << mapper[currentState]; - if ( type == MANUAL_CONTROL_TYPE_TIMED ) { - std::cout << left << setw(8) << setfill(' ') << "Time Remaining (secs): " << endTime - time(NULL); - } - std::cout.flush(); -} \ No newline at end of file
--- a/ICE-Application/src/ConfigurationHandler/Controls/ManualControl.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +0,0 @@ -/****************************************************************************** - * - * File: ManualControl.h - * Desciption: ICE Manual Control Class - * - *****************************************************************************/ -#ifndef MANUALCONTROL_H -#define MANUALCONTROL_H - -#include <string> -#include <stdio.h> - -#define MANUAL_CONTROL_TYPE_NONE 0 -#define MANUAL_CONTROL_TYPE_CONTINUOUS 1 -#define MANUAL_CONTROL_TYPE_TIMED 2 -#define MANUAL_CONTROL_TYPE_SETPOINT 3 - -typedef enum { - MANUAL_CONTROL_OK, - MANUAL_CONTROL_ERROR, - MANUAL_CONTROL_DESTROY -} ManualControlError_t; - -//! Manual Control - implements a manual control. -/// Each output, relay or analog, can be put into manual mode. Manual mode -/// overrides automatic control of the output. A user can invoke manual mode over -/// any output. -/// -/// Manual On: User chooses the relay and sets the timed duration. The relay -/// is turned ON. After the timed interval elapses, the output returns to its -/// previous highest-prirority control. If the timed duratation is set to 0, -/// the relay is turned on indefinitely. -/// -/// Manual Off: Users chooses the relay and sets the timed duration The relay -/// is turned OFF. After the timed interval elapses, the output returns to its -/// previous highes-priority control. If the timed duration is set to 0, the -/// relay is turned off indefinitley. -/// @code -/// #include "ManualControl.h" -/// -/// int main(void) -/// { -/// ManualControl *m = new ManualControl(); -/// bool rc = m->load(controlFile); -/// if ( rc != true ) { -/// logError("%s: failed to load the manual control\n"); -/// delete m; -/// exit(-1); -/// } -/// -/// m->start(); -/// for ( ;; ) { -/// m->update(); -/// } -/// } -/// @endcode - -class ManualControl -{ -private: - std::string controlFile; // name of the control file - std::string id; // identifier - std::string output; // output - unsigned int type; // timed, continuous, setpoint - unsigned int priority; // control priority - unsigned int duration; // in seconds (not implemented) - float setpoint; // setpoint (not implemented) - unsigned int state; // ON or OFF - unsigned int percent; // analog/manual value - unsigned long endTime; // for timed control - - enum State { - STATE_INIT, // init - STATE_STARTUP, // control has been started - STATE_CONTROL_ON, // control is ON - STATE_CONTROL_OFF, // control is OFF - STATE_CONTROL_FINISHED // (timed) interval complete - }; - State currentState; - - // validate the contents of the control file - bool validateControlData(const char *); - - // assign control data from control file to object data - void copyControlData(const char *); - - // power an output - int powerOutput(void); - -public: - /// create a manual control instance - ManualControl() {}; - - /// destructor - ~ManualControl() { - printf("\r%s invoked\n", __func__); - } - /// load a manual control instance with data from a JSON configuration file - /// @param filename name of the control file - /// @returns - /// true on success, - /// false on failure - bool load(std::string filename); - - /// start a manual control instance - void start(void); - - /// update a manual control instance (implements a simple state machine) - /// @returns - /// MANUAL_CONTROL_OK, - /// MANUAL_CONTROL_ERROR, - /// MANUAL_CONTROL_DESTROY - ManualControlError_t update(void); - - /// unregister a manual control from the output thread - int unregisterControl(void); - - /// display pertinent data of a manual control instance - void display(void); - - /// get control file - std::string getControlFile(void) const { - return controlFile; - } - - /// get control identifier - std::string getId(void) const { - return id; - } - - /// get associated output - std::string getOutput(void) const { - return output; - } - - /// get manual control type - unsigned int getType(void) const { - return type; - } - - /// get control priority - unsigned int getPriority(void) const { - return priority; - } - - /// get control duration (timed) - unsigned int getDuration(void) const { - return duration; - } - - /// get setpoint (unimplemented) - float getSetpoint(void) const { - return setpoint; - } - - /// get manual control FSM state - unsigned int getState(void) const { - return state; - } - - /// get analog out % - unsigned int getPercent(void) const { - return percent; - } -}; - -#endif
--- a/ICE-Application/src/ConfigurationHandler/Controls/PIDControl.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -// placeholder for PID control
--- a/ICE-Application/src/ConfigurationHandler/Controls/PIDControl.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -// placeholder for PID control
--- a/ICE-Application/src/ConfigurationHandler/Controls/SensorErrorControl.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,418 +0,0 @@ -/****************************************************************************** -* -* File: SensorErrorControl.cpp -* Desciption: ICE Sensor Error Control class implementation -* -*****************************************************************************/ -#include "SensorErrorControl.h" -#include "cJSON.h" -#include "ModbusMasterApi.h" -#include "utilities.h" -#include "ICELog.h" -#include <string> -#include <iostream> -#include <iomanip> -#include <stdarg.h> - -using namespace std; - -// for debugging - this can be set via the console command: -// "debug-se 1 -bool debugSensorError = false; - -static void debug(const char *fmt, ...) -{ - if ( debugSensorError ) { - va_list vargs; - va_start(vargs, fmt); - vfprintf(stdout, fmt, vargs); - va_end(vargs); - } -} - -// -// method: load -// description: load data from the control file -// -// @param[in] _controlFile -> the control file -// @return true if loaded; false otherwise -// -bool SensorErrorControl::load(const std::string _controlFile) -{ - controlFile = _controlFile; - - // read the data into a buffer - char dataBuf[MAX_FILE_SIZE]; - - bool rc = GLOBAL_mdot->readUserFile(controlFile.c_str(), (void *)dataBuf, sizeof(dataBuf)); - if ( rc != true ) { - logError("%s: failed to read %s", __func__, controlFile.c_str()); - // caller should destroy the object - return false; - } - - // validate the JSON formatted control data - if ( !validateControlData(dataBuf) ) { - logError("%s: failed to validate control data", __func__); - return false; - } - - // copy the control data - copyControlData(dataBuf); - - ModbusValue val; - // validate the input - if ( ModbusMasterReadRegister(input, &val) == false ) { - logError("%s failed to find input %s", id.c_str(), input.c_str()); - return false; - } - - isVirtualOutput = Util_isVirtualOutput(output) ? true : false; - return true; -} - -// -// @method: validateControlData -// @description: vaqlidate the JSON formatted string -// -// @param[in] buf -> JSON formatted string -// @param[out] none -// @return true if valid; false otherwise -// -bool SensorErrorControl::validateControlData(const char *buf) -{ - bool rc = true; - - // parse the data - cJSON * root = cJSON_Parse(buf); - - if ( !cJSON_HasObjectItem(root, "id") || - !cJSON_HasObjectItem(root, "priority") || - !cJSON_HasObjectItem(root, "input") || - !cJSON_HasObjectItem(root, "output") || - !cJSON_HasObjectItem(root, "dutyCycle") || - !cJSON_HasObjectItem(root, "interval") ) { - logError("%s: control file is missing expected tags", __func__); - rc = false; - } - - cJSON_Delete(root); - return rc; -} - -// -// method: copyControlData -// description: copy the data from the control file to the object -// -// @param[in] buf -> JSON formatted string -// @param[out] none -// @return none -// -void SensorErrorControl::copyControlData(const char *buf) -{ - cJSON *root = cJSON_Parse(buf); - - id = cJSON_GetObjectItem(root, "id")->valuestring; - priority = atoi(cJSON_GetObjectItem(root, "priority")->valuestring); - input = cJSON_GetObjectItem(root, "input")->valuestring; - output = cJSON_GetObjectItem(root, "output")->valuestring; - dutyCycle.dutyCycle = atoi(cJSON_GetObjectItem(root, "dutyCycle")->valuestring); - dutyCycle.interval = atoi(cJSON_GetObjectItem(root, "interval")->valuestring); - - cJSON_Delete(root); -} - -// -// @method: start -// @description: put the control in the start state -// -// @param[in] none -// @param[out] none -// @return none -// -void SensorErrorControl::start(void) -{ - currentState = STATE_START; -} - -// -// @method update -// @description run the simplified state machine -// -// @param[in] none -// @param[out] none -// @return OK on success; error otherwise -// -SensorErrorControlError_t SensorErrorControl::update(void) -{ - SensorErrorControlError_t rc = SENSOR_ERROR_CONTROL_OK; - - switch (this->currentState) { - case STATE_INIT: - // do nothing: control must be programmatically started - break; - case STATE_START: - if ( this->isSensorError() ) { - if ( dutyCycle.dutyCycle > 0 ) { - this->startDutyTimer(); - this->currentState = STATE_DUTY_CYCLE_ON; - sendMailToOutput(ACTION_CONTROL_ON); - debug("\r%s: [STARTUP]->sensor error->[DUTY_CYCLE_ON] time=%lu\n", id.c_str(), time(0)); - } else { - // this is a fixed-off state - this->stopDutyTimer(); // probably not needed - this->currentState = STATE_DUTY_CYCLE_OFF; - // tell the output task to turn off the relay - sendMailToOutput(ACTION_CONTROL_OFF); - debug("\r%s: [STARTUP]->sensor error (no duty)->[DUTY_CYCLE_OFF] %lu\n", id.c_str(), time(0)); - } - } else { - // input signal is OK - debug("\r%s: [STARTUP]->OK->[OFF] %lu\n", id.c_str(), time(0)); - this->currentState = STATE_CONTROL_OFF; - } - break; - case STATE_CONTROL_OFF: - if ( isSensorError() ) { - if ( dutyCycle.dutyCycle > 0 ) { - this->startDutyTimer(); - this->currentState = STATE_DUTY_CYCLE_ON; - sendMailToOutput(ACTION_CONTROL_ON); - debug("\r%s: [STARTUP]->sensor error->[DUTY_CYCLE_ON] %lu\n", id.c_str(), time(0)); - } else { - // this is a fixed off state - this->stopDutyTimer(); // probably not needed - this->currentState = STATE_DUTY_CYCLE_OFF; - // tell the output task to turn off the relay - sendMailToOutput(ACTION_CONTROL_OFF); - debug("\r%s: [STARTUP]->sensor error (no duty)->[DUTY_CYCLE_OFF] %lu\n", id.c_str(), time(0)); - } - } else { - // input signal is OK, so don't do anything - } - break; - case STATE_DUTY_CYCLE_ON: - if ( !isSensorError() ) { - this->stopDutyTimer(); - this->currentState = STATE_CONTROL_OFF; - this->unregisterControl(); - debug("\r%s: [DUTY_CYCLE_ON]->no error->[OFF] %lu\n", id.c_str(), time(0)); - - } else if ( dutyOnExpired() ) - if ( dutyCycle.dutyCycle < 100 ) { - this->currentState = STATE_DUTY_CYCLE_OFF; - sendMailToOutput(ACTION_CONTROL_OFF); - debug("\r%s: [DUTY_CYCLE_ON]->on expired->[DUTY_CYCLE_OFF] %lu\n", id.c_str(), time(0)); - } else { - // fixed-on state - // do nothing - we'll need to wait for the sensor error - // to clear before something can happen - } - break; - case STATE_DUTY_CYCLE_OFF: - if ( !isSensorError() ) { - // there is no sensor error, so unregister the control and cleanup - this->stopDutyTimer(); - this->currentState = STATE_CONTROL_OFF; - this->unregisterControl(); - debug("\r%s: [DUTY_CYCLE_OFF]->no error->[OFF] %lu\n", id.c_str(), time(0)); - stopDutyTimer(); - } else if ( dutyOffExpired() ) { - if ( dutyCycle.dutyCycle > 0 ) { - // we go back to the OFF state so the start and expiration - // times are recalculated - this->currentState = STATE_CONTROL_OFF; - sendMailToOutput(ACTION_CONTROL_OFF); - debug("\r%s: [DUTY_CYCLE_OFF]->off expired->[OFF] %lu\n", id.c_str(), time(0)); - } else { - // fixed-off state - // do nothing - we'll need to wait for the sensor error - // to clear before something can happen - } - } - break; - default: - logError("%s: unknown state\n", __func__); - rc = SENSOR_ERROR_CONTROL_UNK_STATE; - break; - } - return rc; -} - -// -// @method: isSensorError -// @description: reads the modbus register to get the error condition of -// the input signal -// -// @param[in] none -// @param[out] none -// @return true if in error; false otherwise -// -bool SensorErrorControl::isSensorError() -{ - ModbusValue val; - - if ( ModbusMasterReadRegister(this->input, &val) != true ) { - logError("Failed to read %s\n", this->input.c_str()); - return true; - } else if ( val.errflag ) { - // input is in sensor error - return true; - } - return false; -} - -// -// @method: dutyOnExpired -// @description: returns true of duty ON time expired -// -// @param[in] none -// @param[out] none -// @return true if expired; false otherwise -// -bool SensorErrorControl::dutyOnExpired(void) -{ - return (time(0) >= duty_timer.offTime); -} - -// -// @method: dutyOffExpired -// @description: returns true of duty OFF time expired -// -// @param[in] none -// @param[out] none -// @return true if expired; false otherwise -// -bool SensorErrorControl::dutyOffExpired(void) -{ - return (duty_timer.expirationTime < time(0)); -} - -// -// method: startDutyTimer -// descrption: start the sensor error duty-timer -// -// @param none -// @return none -// -void SensorErrorControl::startDutyTimer(void) -{ - unsigned long currentTime = time(0); - unsigned long period = dutyCycle.interval * 60; - - duty_timer.offTime = currentTime + ((double)period * ((double)dutyCycle.dutyCycle/100.0)); - duty_timer.expirationTime = currentTime + period; - - debug("\r%s:%s-> currentTime = %lu\n", __func__, id.c_str(), currentTime); - debug("\r%s:%s-> off Time = %lu\n", __func__, id.c_str(), duty_timer.offTime); - debug("\r%s:%s-> expiration = %lu\n", __func__, id.c_str(), duty_timer.expirationTime); -} - -// -// method: stopDutyTimer -// descrption: stop the sensor error duty-timer -// -// @param none -// @return none -// -void SensorErrorControl::stopDutyTimer(void) -{ - memset(&duty_timer, 0, sizeof(duty_timer)); -} - -// -// method: sendMailToOutput -// description: send a message to the output thread -// -// @param[in] action -> on, off, etc. -// @param[out] none -// @return -// -void SensorErrorControl::sendMailToOutput(OutputAction action) -{ - - if ( isVirtualOutput ) { - debug("%s:%s updating the virtual output %s\n", __func__, id.c_str(), output.c_str()); - ModbusMasterWriteRegister(this->output, - (action == ACTION_CONTROL_ON) ? 1.0 : 0.0); - } else { - debug("%s:%s sensor error control attempting to send action %d\n", - __func__, id.c_str(), action); - - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->action = action; - output_mail->controlType = CONTROL_SENSOR_ERROR; - output_mail->priority = this->priority; - - strncpy(output_mail->input_tag, this->input.c_str(), sizeof(output_mail->input_tag)-1); - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - - OutputMasterMailBox.put(output_mail); - } -} - -// -// @method: unregisterControl -// @description: unregister the control with the output task -// -// @param[in] none -// @param[out] none -// @return none -// -void SensorErrorControl::unregisterControl(void) -{ - if ( isVirtualOutput ) { - debug("%s: %s attempting to unregister virtual output %s\n", __func__, id.c_str(), output.c_str()); - ModbusMasterWriteRegister(output, 0.0); - } else { - debug("%s: %s attempting to unregister output %s\n", - __func__, id.c_str(), output.c_str()); - - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->action = ACTION_CONTROL_UNREGISTER; - output_mail->controlType = CONTROL_FAILSAFE; - output_mail->priority = this->priority; - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - - OutputMasterMailBox.put(output_mail); - } -} - -// -// @method: display -// @description: show the pertinents -// -// @param[in] none -// @param[out] none -// @return none -// -void SensorErrorControl::display(void) -{ - const char *mapper[] = { "INIT", - "START", - "OFF", - "DUTY CYCLE ON", - "DUTY CYCLE OFF", - "invalid" - }; - - printf("\r\n"); - std::cout << left << setw(10) << setfill(' ') << "sensor error: "; - std::cout << left << setw(40) << setfill(' ') << controlFile; - std::cout << left << setw(20) << setfill(' ') << id; - std::cout << left << setw(6) << setfill(' ') << priority; - std::cout << left << setw(20) << setfill(' ') << input; - std::cout << left << setw(20) << setfill(' ') << output; - std::cout << left << setw(16) << setfill(' ') << mapper[currentState]; - std::cout << "duty cycle: " << left << setw(4) << setfill(' ') << dutyCycle.dutyCycle; - std::cout << "interval: " << left << setw(4) << setfill(' ') << dutyCycle.interval; - - std::cout.flush(); -} -
--- a/ICE-Application/src/ConfigurationHandler/Controls/SensorErrorControl.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -#ifndef SENSORERRORCONTROL_H -#define SENSORERRORCONTROL_H - -#include <stdio.h> -#include <string> -#include <time.h> - -#include "global.h" - -typedef enum { - SENSOR_ERROR_CONTROL_OK, - SENSOR_ERROR_CONTROL_UNK_STATE, - SENSOR_ERROR_CONTROL_ERROR, -} SensorErrorControlError_t; - -//! SensorErrorControl - implements a sensor error control by reading an input's -// error flag and manipulating an output (duty-cycle style) -class SensorErrorControl -{ -private: - std::string controlFile; // the control file - std::string id; // unique control identifier - std::string input; // sensor to read - std::string output; // output to manipulate - unsigned int priority; // control priority (typically 450) - - typedef struct dutyCycle_tag { - unsigned int dutyCycle; // on-time (percentage) - unsigned int interval; // in minutes - } dutyCycle_t; - - dutyCycle_t dutyCycle; - - typedef struct timer_tag { - unsigned long offTime; // epoch time - unsigned long expirationTime; // epoch time - } Timer_t; - - Timer_t duty_timer; // the duty timer - - enum State { - STATE_INIT, // object instantiated - STATE_START, // control has been started - STATE_CONTROL_OFF, // control is not above/below limit - STATE_DUTY_CYCLE_ON, // in sensor error, duty-cycle ON - STATE_DUTY_CYCLE_OFF, // in sensor error, duty-cycle OFF - STATE_MAX - }; - - State currentState; // current state - bool isVirtualOutput; // output is virtual - - bool validateControlData(const char *buf); // validate JSON control data - void copyControlData(const char *buf); // copy JSON data to control data - - bool isSensorError(void); // input sig is in sensor error - - void startDutyTimer(void); // start the duty timer - void stopDutyTimer(void); // stop the duty timer - - bool dutyOnExpired(void); // boolean check if duty ON-time expired - bool dutyOffExpired(void); // boolean check if duty OFF-time expired - - void sendMailToOutput(OutputAction action); -public: - /// Create a sensor-error control instance - SensorErrorControl() { - currentState = STATE_INIT; - isVirtualOutput = false; - } - /// Destroy a sensor-error control instance - ~SensorErrorControl() { - // "erased...from existence!" -- Doc Brown - printf("\r%s invoked\n", __func__); - } - - /// load a sensor-error control instance with data from a JSON configuration file - bool load(std::string filename); - - /// start a sensor-error control instance - void start(void); - - /// update this sensor error control instance - SensorErrorControlError_t update(void); - - /// unregister a failsafe control instance - void unregisterControl(void); - - /// display the pertinent data of this sensor error control instance - void display(void); - - /// get control file - std::string getControlFile() const { - return controlFile; - } - - /// get control identifier - std::string getId() const { - return id; - } -}; - -#endif // SENSORERRORCONTROL_H -
--- a/ICE-Application/src/ConfigurationHandler/Controls/SequenceControl.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,546 +0,0 @@ -/****************************************************************************** -* -* File: SequenceControl.cpp -* Desciption: ICE Sequence Control class implementation -* -*****************************************************************************/ - -#include "SequenceControl.h" -#include "ICELog.h" -#include "cJSON.h" -#include "global.h" - -#include <stdlib.h> -#include <vector> -#include <string> -#include <iostream> -#include <iomanip> -#include <stdarg.h> -#include <assert.h> -#include "ModbusMasterApi.h" -#include "ConfigurationHandler.h" - -// for debugging - this can be set via the console command: -// "debug-se 1 -bool debugSequenceControl = false; - -static void debug(const char *fmt, ...) -{ - if ( debugSequenceControl ) { - va_list vargs; - va_start(vargs, fmt); - vfprintf(stdout, fmt, vargs); - va_end(vargs); - } -} - -// -// method: load -// decsription: load data from the control file -// -// @param[in] _controlFile -> the control file -// @param[out] none -// @return true on success; false on error -// -bool SequenceControl::load(std::string _controlFile) -{ - controlFile = _controlFile; - - // read the data into a buffer - char dataBuf[MAX_FILE_SIZE*3]; - bool rc = GLOBAL_mdot->readUserFile(controlFile.c_str(), (void *)dataBuf, sizeof(dataBuf)); - if ( rc != true ) { - logError("%s: failed to read %d bytes from %s", __func__, sizeof(dataBuf), controlFile.c_str()); - // caller should destroy the object - return false; - } - - // validate the JSON tags in the control file - if ( validateControlData(dataBuf) != true ) { - logError("%s: invalid control data.", __func__); - return false; - } - - // assign object data from control file - copyControlData(dataBuf); - - // TODO: perform additional validation - - return true; -} - - - -// method: validateControlData -// description: validates the data in the control file -// -// @param[in] dataBuf -> JSON formatted string -// @param[out] non -// @return true if valid; false otherwise -// -bool SequenceControl::validateControlData(const char *dataBuf) -{ - // parse the json data - bool rc = true; - cJSON * root = cJSON_Parse(dataBuf); - - // parse the control header - if ( !cJSON_HasObjectItem(root, "id") || - !cJSON_HasObjectItem(root, "startTrigger") || - !cJSON_HasObjectItem(root, "sequence") ) { - logError("Sequence Control is missing expected tags"); - cJSON_Delete(root); - return false; - } - - // validate the sequence table - cJSON *sequenceTable = cJSON_GetObjectItem(root, "sequence"); - for ( int i = 0; i < cJSON_GetArraySize(sequenceTable); ++i ) { - cJSON *entry = cJSON_GetArrayItem(sequenceTable, i); - if ( !cJSON_HasObjectItem(entry, "startTrigger") ) { - logError("Sequence Table missing startTrigger tag"); - rc = false; - break; - } - if ( !cJSON_HasObjectItem(entry, "actions") ) { - logError("Sequence table is missing actions tag"); - rc = false; - // FIXME: finish the internals - break; - } - if ( !cJSON_HasObjectItem(entry, "stopTrigger") ) { - logError("Sequence table is missing stopTrigger tag"); - rc = false; - break; - } - } - - cJSON_Delete(root); - return rc; -} - -// -// method: copyControlData -// description: copy JSON formatted control data to object -// -// @param[in] dataBuf -> JSON formatted data -// @param[out] none -// @return none -// -void SequenceControl::copyControlData(const char* dataBuf) -{ - cJSON *root = cJSON_Parse(dataBuf); - - id = cJSON_GetObjectItem(root, "id")->valuestring; - startTrigger = cJSON_GetObjectItem(root, "startTrigger")->valuestring; - - // validate the sequence table - cJSON *sequenceTable = cJSON_GetObjectItem(root, "sequence"); - for ( int i = 0; i < cJSON_GetArraySize(sequenceTable); ++i ) { - cJSON *entry = cJSON_GetArrayItem(sequenceTable, i); - - SequenceEntry n; - - n.startTrigger = cJSON_GetObjectItem(entry, "startTrigger")->valuestring; - n.stopTrigger = cJSON_GetObjectItem(entry, "stopTrigger")->valuestring; - - cJSON *actionList = cJSON_GetObjectItem(entry, "actions"); - for ( int j = 0; j < cJSON_GetArraySize(actionList); ++j ) { - cJSON *list = cJSON_GetArrayItem(actionList, j); - Action_t a; - a.action = cJSON_GetObjectItem(list, "action")->valuestring; - a.id = cJSON_GetObjectItem(list, "id")->valuestring; - if ( a.action == "assign" ) { - a.value = atof(cJSON_GetObjectItem(list, "val")->valuestring); - } else { - a.value = 0; - } - n.actions.push_back(a); - } - // push this entry onto this sequence table - this->sequenceTable.push_back(n); - } - - cJSON_Delete(root); -} - -// -// method: start -// decsription: start the sequence control -// -// @param[in] none -// @param[out] none -// @return none -// -void SequenceControl::start(void) -{ - currentState = SEQ_STATE_START; -} - -// -// method: run -// decsription: run the sequence control (performs the updates) -// -// @param[in] none -// @param[out] none -// @return OK on success; error otherwise -// -SequenceControlError_t SequenceControl::run(void) -{ - SequenceControlError_t rc = SEQUENCE_CONTROL_OK; - - switch ( this->currentState ) { - case SEQ_STATE_INIT: - // do nothing - break; - case SEQ_STATE_START: - // here we need to wait for the start trigger to happen - if ( this->isControlStartTriggerOn() ) { - debug("\r%s: [START]->start trigger->[LOADING]\n", id.c_str()); - ModbusValue val; - ModbusMasterReadRegister(startTrigger, &val); - debug("\r%s:%s: val.value = %f\n", id.c_str(), startTrigger.c_str(), val.value); - currentState = SEQ_STATE_LOADING_NEXT_ENTRY; - } else { - // just continue waiting for the start trigger to fire - } - break; - case SEQ_STATE_LOADING_NEXT_ENTRY: { - // this is a transient state - bool rc = this->loadNextEntry(); - if ( rc ) { - debug("\r%s: [LOADING]->next entry->[WAIT-START]\n", id.c_str()); - currentState = SEQ_STATE_WAITING_START; - } else { - debug("\r%s: [LOADING]->no entries->[FINISHED]\n", id.c_str()); - currentState = SEQ_STATE_FINISHED; - } - break; - } - case SEQ_STATE_WAITING_START: - // wait for the start triggers to evaluate to true - if ( this->isCurrentEntryStartTriggerOn() ) { - debug("\r%s: [WAIT-START]->perform actions->[WAIT-STOP]\n", id.c_str()); - performActions(); - currentState = SEQ_STATE_WAITING_STOP; - } else { - // wait until the start trigger fires - } - break; - case SEQ_STATE_WAITING_STOP: - // wait for the stop triggers to evaluate to true - if ( this->isCurrentEntryStopTriggerOn() ) { - debug("\r%s: [WAIT-STOP]->stop trigger ON->[LOAD NEXT]\n", id.c_str()); - currentState = SEQ_STATE_LOADING_NEXT_ENTRY; - } else { - // continue waiting - } - break; - case SEQ_STATE_FINISHED: - // do any cleanup work that's needed here. - debug("\r%s: [FINISHED]->clearing the reg->[START]\n", id.c_str()); - ModbusMasterWriteRegister(this->startTrigger, 0); - this->currentState = SEQ_STATE_START; - break; - case SEQ_STATE_MAX: - default: - logError("%s: unknown state %u\n", __func__, this->currentState); - rc = SEQUENCE_CONTROL_UNK_STATE; - break; - } - return rc; -} - -// -// method: isControlStartTriggerOn -// description: true if the start trigger evals to true -// -// @param[in] none -// @param[out] none -// @return true if start trigger > 0; false otherwise -// -bool SequenceControl::isControlStartTriggerOn(void) -{ - ModbusValue value; - bool rc = ModbusMasterReadRegister(this->startTrigger, &value); - if ( rc && value.value ) { - return true; - } - - return false; -} - -// -// method: isCurrentEntryStartTriggerOn -// description; true the current sequence entry start trigger evals to true -// -// @param[in] none -// @param[out] none -// @return none -// -bool SequenceControl::isCurrentEntryStartTriggerOn(void) -{ - ModbusValue value; - bool rc = ModbusMasterReadRegister(this->currentEntry.startTrigger, &value); - if ( rc != true ) { - logError("%s: failed to read %s from modbus master", - __func__, this->currentEntry.startTrigger.c_str()); - return rc; - } else if ( value.value ) { - debug("\r%s:%s-> returning true\n", __func__, id.c_str()); - return true; - } - return false; -} - -// -// method: isCurrentEntryStopTriggerOn -// description; true the current sequence entry start trigger evals to true -// -// @param[in] none -// @param[out] none -// @return none -// -bool SequenceControl::isCurrentEntryStopTriggerOn(void) -{ - ModbusValue value; - bool rc = ModbusMasterReadRegister(this->currentEntry.stopTrigger, &value); - if ( rc != true ) { - logError("%s: failed to read %s from modbus master", - __func__, this->currentEntry.stopTrigger.c_str()); - return rc; - } else if ( value.value ) { - debug("\r%s:%s-> returning true\n", __func__, id.c_str()); - return true; - } - return false; -} - -// -// method: loadNextEntry -// description: load the next entry from the sequence table -// -// @param[in] none -// @param[out[ none -// @return true is loaded; false otherwise -// -bool SequenceControl::loadNextEntry(void) -{ - if ( this->sequenceTable.empty() ) { - debug("\r%s: sequence table is empty\n", id.c_str()); - return false; - } - if ( nextEntryIndex < this->sequenceTable.size() ) { - currentEntry = sequenceTable.at(nextEntryIndex++); - printf("\r...successfully loaded new entry\n"); - return true; - } - return false; -} - -// -// method: performActions -// description: perform the actions associated with the current entry -// -// @param[in] none -// @param[out] none -// @return none -// -SequenceControlError_t SequenceControl::performActions(void) -{ - SequenceControlError_t rc = SEQUENCE_CONTROL_OK; - - // possible action types: - // create -> create a control - // delete -> delete a control - // modify -> modify a control - // assign -> assign a value to a modbus register - // execute -> execute a script - if ( !currentEntry.actions.empty() ) { - std::vector<Action_t>::const_iterator pos; - for ( pos = currentEntry.actions.begin(); pos != currentEntry.actions.end(); ++pos ) { - debug("\raction->%s on ID->%s\n", pos->action.c_str(), pos->id.c_str()); - // based on the action, we need to determine what to do - // 1: determine the action - // 2: see if the file exists (catastrophic) - // 3: if it's a create or delete action, send a message to the - // configuration handler - if ( pos->action == "createsp" ) { - rc = createSubControl(pos->id, CONTROL_SETPOINT); - assert(!rc); - } else if ( pos->action == "createtm" ) { - rc = createSubControl(pos->id, CONTROL_TIMER); - assert(!rc); - } else if ( pos->action == "deletesp" ) { - rc = destroySubControl(pos->id, CONTROL_SETPOINT); - assert(!rc); - } else if ( pos->action == "deletetm" ) { - rc = destroySubControl(pos->id, CONTROL_TIMER); - assert(!rc); - } else if ( pos->action == "execute" ) { - // not implemented - } else if ( pos->action == "assign" ) { - if ( assignRegister(pos->id, pos->value) != true ) { - assert(0); - } - } else { - logError("%s: unknown action %s (%s)", __func__, pos->action.c_str(), pos->id.c_str()); - rc = SEQUENCE_CONTROL_BAD_ACTION; - } - } - } else { - logInfo("%s: no entries in the action table", __func__); - } - return rc; -} - -// -// method: createSubControl -// description: create a control that's listed in the sequence table -// -// @param[in] action (create, destroy, etc.) -// @param[in] id -> control identifier -// @param[out[ none -// @return OK on success; error otherwise -// -SequenceControlError_t SequenceControl::createSubControl(const std::string controlId, - Control_t type) -{ - std::string file_prefix; - - switch (type) { - case CONTROL_SETPOINT: - file_prefix = CONTROL_SP_STR; - break; - case CONTROL_TIMER: - file_prefix = CONTROL_TM_STR; - break; - default: - logError("%s %s unsupported type %d", __func__, id.c_str(), type); - return SEQUENCE_CONTROL_BAD_CONTROL_TYPE; - } - - char filename[64]; - snprintf(filename, sizeof(filename), "%s%s_%s%s.json", - SEQUENCE_CONTROL_FILENAME_PREFIX, - this->id.c_str(), - file_prefix.c_str(), - controlId.c_str()); - - debug("\r%s: searching for %s\n", id.c_str(), filename); - - // send a message to the configuration handler to create the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_CREATE; - msg->control = (Control_t)type; - strncpy(msg->controlFile, filename, sizeof(msg->controlFile)-1); - - printf("%s: Sending a create request for control %s\r\n", - __func__, msg->controlFile); - - ConfigHandlerMailBox.put(msg); - - return SEQUENCE_CONTROL_OK; -} - -// -// method: destroySubControl -// description: destroy a control that's listed in the sequence table -// -// @param[in] action (create, destroy, etc.) -// @param[in] id -> control identifier -// @param[out[ none -// @return OK on success; error otherwise -// -SequenceControlError_t SequenceControl::destroySubControl(const std::string controlId, - Control_t type) -{ - std::string file_prefix; - - switch (type) { - case CONTROL_SETPOINT: - file_prefix = CONTROL_SP_STR; - break; - case CONTROL_TIMER: - file_prefix = CONTROL_TM_STR; - break; - default: - logError("%s %s unsupported type %d", __func__, id.c_str(), type); - return SEQUENCE_CONTROL_BAD_CONTROL_TYPE; - } - - char filename[64]; - snprintf(filename, sizeof(filename), "%s%s_%s%s.json", - SEQUENCE_CONTROL_FILENAME_PREFIX, - this->id.c_str(), - file_prefix.c_str(), - controlId.c_str()); - - debug("\r%s: searching for %s\n", id.c_str(), filename); - - // send a message to the configuration handler to create the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_DESTROY; - msg->control = (Control_t)type; - strncpy(msg->controlFile, filename, sizeof(msg->controlFile)-1); - - printf("%s: Sending a create request for control %s\r\n", - __func__, msg->controlFile); - - ConfigHandlerMailBox.put(msg); - - return SEQUENCE_CONTROL_OK; -} - -bool SequenceControl::assignRegister(const std::string id, float value) -{ - return (ModbusMasterWriteRegister(id, value)); -} - -// -// method: display -// decsription: display the control data -// -// @param[in] none -// @param[out] none -// @return none -// -void SequenceControl::display(void) -{ - const char *mapper[] = { "INIT", - "START", - "LOADING", - "WAIT-START", - "WAIT-STOP", - "FINISHED", - "FAILED", - "NULL" - }; - - printf("\rid-> %s\n", id.c_str()); - printf("\rstartTrigger-> %s\n", startTrigger.c_str()); - printf("\rSequenceTable: \n"); - - std::vector<SequenceEntry>::const_iterator seqIt; - - int entry = 0; - - for ( seqIt = sequenceTable.begin(); seqIt != sequenceTable.end(); ++seqIt ) { - printf("\rEntry %d: %s\n \r startTrigger: %s\n", - ++entry, - (entry == nextEntryIndex-1) ? "<-currently active" : "", - seqIt->startTrigger.c_str()); - std::vector<Action_t>::const_iterator actIt; - for ( actIt = seqIt->actions.begin(); actIt != seqIt->actions.end(); ++actIt ) { - printf("\r\taction-> %s, id-> %s ", actIt->action.c_str(), actIt->id.c_str()); - if ( actIt->action == "assign" ) printf("value -> %f", actIt->value); - printf("\r\n"); - } - printf("\r stopTrigger: %s\n", seqIt->stopTrigger.c_str()); - } - - printf("\r\n\rcurrent state = %s\r\n\r\n", mapper[this->currentState]); -} -
--- a/ICE-Application/src/ConfigurationHandler/Controls/SequenceControl.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,139 +0,0 @@ -/****************************************************************************** - * - * File: SetpointControl.h - * Desciption: ICE Timer Control Class - * - *****************************************************************************/ -#ifndef SEQUENCECONTROL_H -#define SEQUENCECONTROL_H - -#include <global.h> -#include <string> -#include <stdio.h> -#include <vector> - -#define SEQUENCE_CONTROL_DEBUG -#define SEQUENCE_CONTROL_FILENAME_PREFIX "seq_" - -typedef enum { - SEQUENCE_CONTROL_OK, - SEQUENCE_CONTROL_UNK_STATE, - SEQUENCE_CONTROL_ERROR, - SEQUENCE_CONTROL_DESTROY, - SEQUENCE_CONTROL_BAD_CONTROL_TYPE, - SEQUENCE_CONTROL_BAD_ACTION -} SequenceControlError_t; - -//! SequenceControl - implements a sequence control that is comprised of -// sub-controls and operations to achieve a sequence of activities. -class SequenceControl -{ -private: - std::string controlFile; // the control file - std::string id; // unique identifier - std::string startTrigger; // start trigger: once the start - // trigger evals to true, the - // control will start executing - // the entries in the sequence table - - typedef struct Action_tag { - std::string action; // create/delete/execute - std::string id; // control, vreg or script identifier - float value; // for assignment action - } Action_t; - - // A Sequence Entry: - // - // startTrigger -> an expression that evals to true - // actions[] -> list of actions to perform when the start condition is satisfied - // stopTrigger -> an expression that evals to true - // - typedef struct SequenceEntry_tag { - std::string startTrigger; // trigger to load the sequence - std::vector<Action_t> actions; // actions to perform - std::string stopTrigger; // trigger to load the next entry - } SequenceEntry; - - // the sequence table - std::vector<SequenceEntry> sequenceTable; - - // state machine information - enum SequenceControlState_t { - SEQ_STATE_INIT, // object successfully created - SEQ_STATE_START, // object programmatically started - SEQ_STATE_LOADING_NEXT_ENTRY, // loading the next entry (transient) - SEQ_STATE_WAITING_START, // waiting for entry start trigger(s) - SEQ_STATE_WAITING_STOP, // waiting for entry stop trigger(s) - SEQ_STATE_FINISHED, // finished with all entries in the sequence table - SEQ_STATE_FAILED, // something went awry - SEQ_STATE_MAX - }; - - SequenceControlState_t currentState; // control's current state - SequenceEntry currentEntry; // currently loaded entry in the seq table - unsigned int nextEntryIndex; // next entry index - - /// validate the contents of the control file - bool validateControlData(const char *); - - /// assign control data from control file to object data - void copyControlData(const char *); - - /// has the control starting trigger fired? - bool isControlStartTriggerOn(void); - - /// has the sequence entry starting trigger fired? - bool isCurrentEntryStartTriggerOn(void); - - /// has the sequence entry stopping trigger fired? - bool isCurrentEntryStopTriggerOn(void); - - /// load the next entry from the sequence table - bool loadNextEntry(void); - - /// perform the actions from the sequence entry - SequenceControlError_t performActions(void); - - /// create a control from the sequence table - SequenceControlError_t createSubControl(const std::string controlId, - Control_t type); - - /// delete a control from the sequence table - SequenceControlError_t destroySubControl(const std::string controlId, - Control_t type); - - /// assign a virtual register - bool assignRegister(const std::string id, float value); - -public: - /// create a sequence control - SequenceControl() { - currentState = SEQ_STATE_INIT; - nextEntryIndex = 0; - }; - /// destructor - ~SequenceControl() { - printf("\r%s invoked\n", __func__); - } - - /// load sequence control data from a JSON configuration file - bool load(std::string filename); - - /// start the sequence control - void start(void); - - /// the motor - SequenceControlError_t run(void); - - /// display the pertinents - void display(void); - - /// get sequence control identifier - std::string getId(void) const { return id; } - - /// get starting trigger - std::string getStartTrigger(void) const { return startTrigger; } -}; - -#endif -
--- a/ICE-Application/src/ConfigurationHandler/Controls/SetpointControl.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,369 +0,0 @@ -/****************************************************************************** -* -* File: SetpointControl.cpp -* Desciption: ICE Setpoint Control class implementation -* -*****************************************************************************/ -#include "SetpointControl.h" -#include "ICELog.h" -#include "cJSON.h" -#include "ModbusMasterApi.h" -#include "global.h" -#include "utilities.h" -#include <string> -#include <iostream> -#include <iomanip> -#include <stdarg.h> -#include <stdlib.h> - -// for debugging - this can be set via the console command: -// "debug-sp 1 or debug-sp 0 -bool debugSetpointControl = false; - -static void debug(const char *fmt, ...) -{ - if ( debugSetpointControl ) { - va_list vargs; - va_start(vargs, fmt); - vfprintf(stdout, fmt, vargs); - va_end(vargs); - } -} - -#ifdef MDOT_ICE -extern mDot *GLOBAL_mdot; -#endif - -// -// method: load -// description: open the configuration file and assign data to the -// setpoint control object -// -// @param controlFile -> name of the control file -// @return true if data is assigned; false on error -// -bool SetpointControl::load(std::string _controlFile) -{ - controlFile = _controlFile; - - char dataBuf[MAX_FILE_SIZE]; - - // read the control data - bool rc = GLOBAL_mdot->readUserFile(controlFile.c_str(), (void *)dataBuf, sizeof(dataBuf)); - if ( rc != true ) { - logError("%s: failed to read %s", __func__, controlFile.c_str()); - // caller should destroy the object - return false; - } - - // validate control data - if ( !validateControlData(dataBuf) ) { - logError("%s: failed to validate control data", __func__); - return false; - } - - // copy control data - copyControlData(dataBuf); - - ModbusValue val; - // validate the input & output - if ( ModbusMasterReadRegister(input, &val) == false ) { - logError("%s failed to find the input %s", id.c_str(), input.c_str()); - return false; - } - if ( ModbusMasterReadRegister(output, &val) == false ) { - logError("%s failed to find output %s", id.c_str(), output.c_str()); - return false; - } - - isVirtualOutput = Util_isVirtualOutput(output) ? true : false; - return true; -} - -// -// method: validateControlData -// description: validate JSON formatted string -// -bool SetpointControl::validateControlData(const char *buf) -{ - bool rc = true; - cJSON * root = cJSON_Parse(buf); - - if ( !cJSON_HasObjectItem(root, "id") || - !cJSON_HasObjectItem(root, "priority") || - !cJSON_HasObjectItem(root, "input") || - !cJSON_HasObjectItem(root, "output") || - !cJSON_HasObjectItem(root, "setpoint") || - !cJSON_HasObjectItem(root, "prodfact") || - !cJSON_HasObjectItem(root, "actingDir") || - !cJSON_HasObjectItem(root, "tol") ) { - logError("Setpoint control is missing expected tags\n"); - rc = false; - } - - cJSON_Delete(root); - return rc; -} - -// -// method: copyControlData -// description: copy JSON formatted data -// -void SetpointControl::copyControlData(const char *buf) -{ - cJSON * root = cJSON_Parse(buf); - - id = cJSON_GetObjectItem(root,"id")->valuestring; - priority = atoi(cJSON_GetObjectItem(root,"priority")->valuestring); - input = cJSON_GetObjectItem(root,"input")->valuestring; - output = cJSON_GetObjectItem(root,"output")->valuestring; - setpoint = atof(cJSON_GetObjectItem(root,"setpoint")->valuestring); - productFactor = atof(cJSON_GetObjectItem(root, "prodfact")->valuestring); - actingDir = atoi(cJSON_GetObjectItem(root, "actingDir")->valuestring); - tolerance = atof(cJSON_GetObjectItem(root, "tol")->valuestring); - - cJSON_Delete(root); -} - -// -// method: start -// description: start the setpoint control -// -// @param none -// @return none -// -void SetpointControl::start(void) -{ - // this is the initial state; what else needs to be done?? - this->currentState = STATE_STARTUP; -} - -// -// method: -// description: based on the state of the control, check for -// under limit and over limit values, adjust the -// state accordingly -// -// @param none -// @return none -// -SetpointControlError_t SetpointControl::update(void) -{ - SetpointControlError_t rc = SETPOINT_CONTROL_OK; - - switch (this->currentState) { - case STATE_INIT: - // do nothing - break; - case STATE_STARTUP: - if ( this->underLimit() ) { - // start the feed right away - this->startFeed(); - this->currentState = STATE_CONTROL_ON; - debug("\r%s: [START]->under limit->[ON]\n", id.c_str()); - } else { - this->currentState = STATE_CONTROL_OFF; - this->stopFeed(); - debug("\r%s: [START]->!under limit->[OFF]\n", id.c_str()); - } - break; - case STATE_CONTROL_ON: - if ( this->overLimit() ) { - // stop the feed - this->stopFeed(); - this->currentState = STATE_CONTROL_OFF; - debug("\r%s: [ON]->over limit->[OFF]\n", id.c_str()); - } else { - // do nothing - } - break; - case STATE_CONTROL_OFF: - if ( this->underLimit() ) { - // start the feed - this->startFeed(); - this->currentState = STATE_CONTROL_ON; - debug("\r%s: [OFF]->under limit->[ON]\n", id.c_str()); - } else { - // do nothing - } - break; - default: - rc = SETPOINT_CONTROL_UNK_STATE; - break; - } - return rc; -} - -// -// method: overLimit -// description: (see @return) -// -// @param none -// @return true if product is over the upper limit for normal mode -// or under the limit for reverse mode; false otherwise -// -bool SetpointControl::overLimit(void) -{ - ModbusValue value; - ModbusMasterReadRegister( input, &value ); - float flimit; - - if ( !actingDir ) { - flimit = setpoint + tolerance; - return (value.value >= flimit); - } else { - flimit = setpoint - tolerance; - return (value.value <= flimit); - } -} - -// -// method: underLimit -// description: (see @return) -// -// @param none -// @return true if product is under lower limit for normal mode or -// over the upper limit for reverse mode; false otherwise -// -bool SetpointControl::underLimit(void) -{ - ModbusValue value; - ModbusMasterReadRegister( input, &value ); - float flimit; - - if ( !actingDir ) { - flimit = setpoint - tolerance; - return (value.value <= flimit); - } else { - flimit = setpoint + tolerance; - return (value.value >= flimit); - } -} - -// -// method: startFeed() -// description: send ON indication to Output Master for this control's -// relay -// -// @param none -// @return none -// -void SetpointControl::startFeed(void) -{ - logInfo("%s: %s attempting to start feed on relay %s\n", - __func__, controlFile.c_str(), output.c_str()); - - if ( isVirtualOutput ) { - ModbusMasterWriteRegister(output, 1.0); - } else { - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->action = ACTION_CONTROL_ON; - output_mail->controlType = CONTROL_SETPOINT; - strncpy(output_mail->input_tag, this->input.c_str(), sizeof(output_mail->input_tag)-1); - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - output_mail->priority = this->priority; - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - OutputMasterMailBox.put(output_mail); - } -} - -// -// method: stopFeed -// description: send OFF indication to Output Master for this control's -// relay -// -// @param none -// @return none -// -void SetpointControl::stopFeed(void) -{ - logInfo("%s: %s attempting to stop feed on relay %s\n", - __func__, controlFile.c_str(), output.c_str()); - - if ( isVirtualOutput ) { - ModbusMasterWriteRegister(output, 0.0); - } else { - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->action = ACTION_CONTROL_OFF; - output_mail->controlType = CONTROL_SETPOINT; - strncpy(output_mail->input_tag, this->input.c_str(), sizeof(output_mail->input_tag)-1); - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - output_mail->priority = this->priority; - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - OutputMasterMailBox.put(output_mail); - } -} - -// -// method: unregisterControl -// description: send OFF indication to Output Master for this control's -// relay -// -// @param none -// @return none -// -void SetpointControl::unregisterControl(void) -{ - logInfo("%s: %s attempting to unregister %s\n", - __func__, id.c_str(), controlFile.c_str()); - - if ( isVirtualOutput ) { - ModbusMasterWriteRegister(output, 0.0); - } else { - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->action = ACTION_CONTROL_UNREGISTER; - output_mail->controlType = CONTROL_MANUAL; - output_mail->priority = this->priority; - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - - OutputMasterMailBox.put(output_mail); - } -} - -// -// method: display -// description: display the control's information -// -// @param none -// @return none -// -void SetpointControl::display(void) -{ - // NOTE: this mapping must be 1:1 with "State" enumeration in SetpointControl.h - std::string mapper[] = { "INIT", - "STARTUP", - "CONTROL_OFF", - "CONTROL_ON", - "CONTROL_DISABLE", - "CONTROL_PAUSE", - "CONTROL_MAX" - }; - - - ModbusValue inputValue; - ModbusMasterReadRegister(input, &inputValue); - - printf("\r\n"); - std::cout << std::left << std::setw(10) << std::setfill(' ') << "setpoint: "; - std::cout << std::left << std::setw(40) << std::setfill(' ') << controlFile; - std::cout << std::left << std::setw(20) << std::setfill(' ') << id; - std::cout << std::left << std::setw(6) << std::setfill(' ') << priority; - std::cout << std::left << std::setw(20) << std::setfill(' ') << input; - std::cout << std::left << std::setw(20) << std::setfill(' ') << output; - std::cout << std::left << std::setw(8) << std::setfill(' ') << setpoint; - std::cout << std::left << std::setw(12) << std::setfill(' ') << (actingDir ? "direct" : "indirect"); - std::cout << std::left << std::setw(16) << std::setfill(' ') << mapper[currentState]; - std::cout << std::right << std::setw(8) << std::setfill(' ') << setpoint + tolerance << " <- "; - std::cout << std::left << std::setw(8) << std::setfill(' ') << inputValue.value << " -> "; - std::cout << std::left << std::setw(8) << std::setfill(' ') << setpoint - tolerance; - //std::cout << left << setw(10) << setfill(' ') << highFailsafe << " : " << lowFailsafe; - - std::cout.flush(); -} \ No newline at end of file
--- a/ICE-Application/src/ConfigurationHandler/Controls/SetpointControl.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,119 +0,0 @@ -/****************************************************************************** - * - * File: SetpointControl.h - * Desciption: ICE Timer Control Class - * - *****************************************************************************/ -#ifndef SETPOINTCONTROL_H -#define SETPOINTCONTROL_H - -#include <string> -#include <stdio.h> - -typedef enum { - SETPOINT_CONTROL_OK, - SETPOINT_CONTROL_UNK_STATE, - SETPOINT_CONTROL_ERROR, - SETPOINT_CONTROL_DESTROY -} SetpointControlError_t; - -// A setpoint control is a control based on a process variable with respect -// to a user defined control band. The output of this control object is a -// true/false decision (for a relay, true means power on the relay). - -//! SetpointControl - implements a setpoint control -class SetpointControl -{ -private: - std::string controlFile; // name of the control file - std::string id; // control identifier - int priority; // control priority - std::string input; // control input - std::string output; // control output - double setpoint; // setpoint value - double productFactor; - bool actingDir; // acting direction, 1 direct - pH, 0 indirect - inhibitor -#if 0 - double highAlert; - double lowAlert; - double highFailsafe; - double lowFailsafe; -#endif - double tolerance; // the error margining - enum State { - STATE_INIT, - STATE_STARTUP, - STATE_CONTROL_OFF, - STATE_CONTROL_ON, - STATE_DISABLE, - STATE_RELOAD, - STATE_MAX - }; - State currentState; - - bool validateControlData(const char *buf); - void copyControlData(const char *buf); - - bool underLimit(); // check if reading is under the limit - bool overLimit(); // check if reading is over the limit - - void startFeed(void); // start a feed - void stopFeed(void); // stop a feed - - bool isVirtualOutput; // output is virtual -public: - /// create a setpoint control - SetpointControl() { - currentState = STATE_INIT; - }; - /// destroy a setpoint control - ~SetpointControl() { - printf("\r%s invoked\n", __func__); - } - - /// load setpoint control data from a JSON configuration file - bool load(std::string filename); - - /// unregister a setpoint control from the output thread - void unregisterControl(void); - - /// start a setpoint control instance - void start(void); - - /// update a setpoint control instance - SetpointControlError_t update(void); - - //display the pertinents - void display(void); - - std::string getControlFile(void) const { - return controlFile; - } - std::string getId(void) const { - return id; - } - unsigned int getPriority(void) const { - return priority; - } - std::string getInput(void) const { - return input; - } - std::string getOutput(void) const { - return output; - } - float getProductFactor(void) const { - return productFactor; - } - int getActingDir(void) const { - return actingDir; - } - float getSetpoint(void) const { - return setpoint; - } - - State getCurrentState(void) const { - return currentState; - } -}; - -#endif \ No newline at end of file
--- a/ICE-Application/src/ConfigurationHandler/Controls/TimerControl.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,467 +0,0 @@ -/****************************************************************************** - * - * File: TimerControl.cpp - * Desciption: ICE Timer Control Class implementation - * - *****************************************************************************/ -#include "TimerControl.h" -#include "ModbusMasterApi.h" -#include "ICELog.h" -#include "cJSON.h" -#include "global.h" -#include "timerUtils.h" -#include "utilities.h" -#include <string> -#include <iostream> -#include <iomanip> - -// for debugging - this can be set via the console command: -// "debug-sp 1 or debug-sp 0 -bool debugTimerControl = false; - -static void debug(const char *fmt, ...) -{ - if ( debugTimerControl ) { - va_list vargs; - va_start(vargs, fmt); - vfprintf(stdout, fmt, vargs); - va_end(vargs); - } -} - -#ifdef MDOT_ICE -extern mDot *GLOBAL_mdot; -#endif - -// -// method: load -// description: load the pertinents from the control file -// -// @param _controlFile -// @return true if loaded; false otherwise -// -bool TimerControl::load(std::string _controlFile) -{ - controlFile = _controlFile; - - char dataBuf[MAX_FILE_SIZE]; - // read the control data - bool rc = GLOBAL_mdot->readUserFile(controlFile.c_str(), (void *)dataBuf, sizeof(dataBuf)); - if ( rc != true ) { - logError("%s: failed to read %s", __func__, controlFile.c_str()); - return false; - } - - // validate the control data - if ( !validateControlData(dataBuf) ) { - logError("%s: failed to validate control data", __func__); - return false; - } - - // copy the control data - copyControlData(dataBuf); - - ModbusValue val; - // validate the output - if ( ModbusMasterReadRegister(output, &val) == false ) { - logError("%s failed to find %s", id.c_str(), output.c_str()); - return false; - } - - isVirtualOutput = Util_isVirtualOutput(output) ? true : false; - - return true; - -} - -// -// method: validateControlData -// description: validates the data in the control file -// -// @param[in] dataBuf -> JSON formatted string -// @param[out] non -// @return true if valid; false otherwise -// -bool TimerControl::validateControlData(const char *buf) -{ - bool rc = true; - cJSON * root = cJSON_Parse(buf); - - if ( !cJSON_HasObjectItem(root, "id") || - !cJSON_HasObjectItem(root, "output") || - !cJSON_HasObjectItem(root, "priority") || - !cJSON_HasObjectItem(root, "day") || - !cJSON_HasObjectItem(root, "startHour") || - !cJSON_HasObjectItem(root, "startMin") || - !cJSON_HasObjectItem(root, "startSec") || - !cJSON_HasObjectItem(root, "duration") || - !cJSON_HasObjectItem(root, "week")) { - logError("%s: control file is missing expected tags", __func__); - rc = false; - } - cJSON_Delete(root); - return rc; -} - -// -// method: copyControlData -// description: copy JSON formatted control data to object -// -// @param[in] dataBuf -> JSON formatted data -// @param[out] none -// @return none -// -void TimerControl::copyControlData(const char *buf) -{ - cJSON *root = cJSON_Parse(buf); - - id = cJSON_GetObjectItem(root,"id")->valuestring; - output = cJSON_GetObjectItem(root, "output")->valuestring; - priority = (unsigned int)atoi(cJSON_GetObjectItem(root, "priority")->valuestring); - std::string day_str = cJSON_GetObjectItem(root, "day")->valuestring; - - if ( day_str == "sun" ) { - day = DAY_SCHEDULE_SUNDAY_MASK; - } else if ( day_str == "mon" ) { - day = DAY_SCHEDULE_MONDAY_MASK; - } else if ( day_str == "tue" ) { - day = DAY_SCHEDULE_TUESDAY_MASK; - } else if ( day_str == "wed" ) { - day = DAY_SCHEDULE_WEDNESDAY_MASK; - } else if ( day_str == "thu" ) { - day = DAY_SCHEDULE_THURSDAY_MASK; - } else if ( day_str == "fri" ) { - day = DAY_SCHEDULE_FRIDAY_MASK; - } else if ( day_str == "sat" ) { - day = DAY_SCHEDULE_SATURDAY_MASK; - } else { - debug("\r%s:%s-> one-shot timer found\n", __func__, id.c_str()); - day = DAY_SCHEDULE_NOT_SPECIFIED; // one-shot timer - } - - startHour = atoi(cJSON_GetObjectItem(root, "startHour")->valuestring); - startMin = atoi(cJSON_GetObjectItem(root, "startMin")->valuestring); - startSec = atoi(cJSON_GetObjectItem(root, "startSec")->valuestring); - duration = atoi(cJSON_GetObjectItem(root, "duration")->valuestring); - - std::string week_str = cJSON_GetObjectItem(root, "week")->valuestring; - if ( week_str == "every" ) { - week = WEEKLY_CHOICE_EVERY_WEEK; - } else if ( week_str == "first" ) { - week = WEEKLY_CHOICE_FIRST_WEEK; - } else if ( week_str == "second" ) { - week = WEEKLY_CHOICE_SECOND_WEEK; - } else if ( week_str == "third" ) { - week = WEEKLY_CHOICE_THIRD_WEEK; - } else if ( week_str == "fourth" ) { - week = WEEKLY_CHOICE_FOURTH_WEEK; - } else if ( week_str == "last" ) { - week = WEEKLY_CHOICE_LAST_WEEK; - } else if ( week_str == "everyother") { - week = WEEKLY_CHOICE_EVERYOTHER_WEEK; - } else { - week = WEEKLY_CHOICE_NOT_SPECIFIED; // one-shot timer - } - - cJSON_Delete(root); -} - -// -// method: start -// description: initialize the control -// -// @param none -// @return none -// -void TimerControl::start(void) -{ - // calculate the next scheduled start time - if ( day == DAY_SCHEDULE_NOT_SPECIFIED ) { - // start running right away - nextScheduledStartTime = time(0); - debug("\r%s: %s is a one-shot timer.\n", __func__, id.c_str()); - } else { - nextScheduledStartTime = time(0) + calcStartTime(); - } - struct tm * timeinfo; - timeinfo = localtime((time_t*)&nextScheduledStartTime); - debug("Timer %s started, begins at %s", this->id.c_str(), asctime(timeinfo)); - - currentState = STATE_OFF; -} - -// -// method: update -// description: run the state machine -// -// @param none -// @return OK on success; error otherwise -// -TimerControlError_t TimerControl::update(void) -{ - TimerControlError_t rc = TIMER_CONTROL_OK; - - unsigned long currentTime = time(0); - - switch ( this->currentState ) { - case STATE_INIT: - // do nothing; timer is waiting for start signal - break; - case STATE_OFF: { - if ( currentTime >= nextScheduledStartTime && - currentTime <= nextScheduledStartTime + duration ) { - currentState = STATE_RUNNING; - this->startFeed(); - startTime = currentTime; // record the actual start time - debug("\r%s: [OFF]->in timing window->[RUNNING] %s\n", id.c_str(), displayTime()); - } - break; - } - case STATE_RUNNING: - if ( currentTime >= startTime + duration ) { - this->stopFeed(); - currentState = STATE_FINISHED; - debug("\r%s: [RUNNING]->time elapsed->[OFF] %s\n", id.c_str(), displayTime()); - } - break; - case STATE_FINISHED: - if ( day == DAY_SCHEDULE_NOT_SPECIFIED || week == WEEKLY_CHOICE_NOT_SPECIFIED ) { - // one shot-timer, just disable it. - nextScheduledStartTime = 0; - startTime = 0; - currentState = STATE_DISABLED; - debug("\r%s: [FINISHED]->one shot timer->[DISABLED] %s\n", id.c_str(), displayTime()); - } else { - // calculate the next scheduled start time - nextScheduledStartTime = time(0) + calcStartTime(); - startTime = 0; - // unregister with the output task - currentState = STATE_OFF; - debug("\r%s: [FINISHED]->time elapsed->[OFF] %s\n", id.c_str(), displayTime()); - } - this->unregisterControl(); - break; - case STATE_DISABLED: - // do nothing - break; - default: - logError("%s: unknown state %d", __func__, this->currentState); - rc = TIMER_CONTROL_UNK_STATE; - break; - } - return rc; -} - -// -// method: startFeed -// description: signal the output thread to start a feed -// -// @param none -// @return none -void TimerControl::startFeed(void) -{ - logInfo("%s: %s attempting to start feed on relay %s\n", - __func__, controlFile.c_str(), output.c_str()); - - if ( isVirtualOutput ) { - // write to the virtual register map - ModbusMasterWriteRegister(output, 1.0); - } else { - // send a message to the output thread - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->action = ACTION_CONTROL_ON; - output_mail->controlType = CONTROL_TIMER; - output_mail->priority = priority; - - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - OutputMasterMailBox.put(output_mail); - } -} - -// -// method: stopFeed -// description: signal the output thread to stop a feed -// -// @param none -// @return none -// -void TimerControl::stopFeed(void) -{ - logInfo("%s: %s attempting to start feed on relay %s\n", - __func__, controlFile.c_str(), output.c_str()); - - if ( isVirtualOutput ) { - // write to the virtual register map - ModbusMasterWriteRegister(output, 0.0); - } else { - // send a message to the output thread - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->action = ACTION_CONTROL_OFF; - output_mail->controlType = CONTROL_TIMER; - output_mail->priority = priority; - - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - OutputMasterMailBox.put(output_mail); - } -} - -// -// method: unregisterControl -// description: send OFF indication to Output Master for this control's -// relay -// -// @param none -// @return none -// -void TimerControl::unregisterControl(void) -{ - logInfo("%s: %s attempting to unregister %s\n", - __func__, id.c_str(), controlFile.c_str()); - - if ( isVirtualOutput ) { - ModbusMasterWriteRegister(output, 0.0); - } else { - - OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); - memset(output_mail, 0, sizeof(OutputControlMsg_t)); - - output_mail->action = ACTION_CONTROL_UNREGISTER; - output_mail->controlType = CONTROL_TIMER; - output_mail->priority = priority; - strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1); - strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); - - OutputMasterMailBox.put(output_mail); - } -} - -// -// method: calcStarTime -// description: calculate the number of seconds until the next -// timer starts. -// -// @param[in] none -// @param[out] none -// @return none -// -unsigned long TimerControl::calcStartTime(void) const -{ - TSC_SCHED_ELEM schedTimeDate; - TSC_SCHED_ELEM pNowTimeDate; - unsigned long pDiffInSeconds; - CNTL_HR_MIN_STRUCT startTime; - - startTime.hour = startHour; - startTime.minute = startMin; - startTime.second = startSec; - - pNowTimeDate.calTime = time( NULL ); - localtime_r( &pNowTimeDate.calTime, &pNowTimeDate.requestTime ); - - pNowTimeDate.schedule.startDate.year = pNowTimeDate.requestTime.tm_year - 100; /* years since 1900 */ - pNowTimeDate.schedule.startDate.year += 2000; - pNowTimeDate.schedule.startDate.month = pNowTimeDate.requestTime.tm_mon + 1; /* months 1 - 12 */ - pNowTimeDate.schedule.startDate.day = pNowTimeDate.requestTime.tm_mday; - pNowTimeDate.schedule.startTime.hour = pNowTimeDate.requestTime.tm_hour; - pNowTimeDate.schedule.startTime.minute = pNowTimeDate.requestTime.tm_min; - pNowTimeDate.schedule.startTime.second = pNowTimeDate.requestTime.tm_sec; - - /* setup schedTimeDate with the current date, time and the required schedule */ - schedTimeDate.schedule.startDate = pNowTimeDate.schedule.startDate; - schedTimeDate.schedule.startTime = startTime; - schedTimeDate.schedule.dailySchedule = day; - schedTimeDate.schedule.weeklySchedule = (WEEKLY_CHOICES)week; - schedTimeDate.calTime = pNowTimeDate.calTime; - schedTimeDate.requestTime = pNowTimeDate.requestTime; - - TSCutilComputeStartDate( &schedTimeDate ); - Util_dateTimeDiff( &pNowTimeDate.schedule, &schedTimeDate.schedule, &pDiffInSeconds ); - - return pDiffInSeconds; -} - -const char* TimerControl::displayTime(void) -{ - struct tm * timeinfo; - time_t currentTime = time(0); - timeinfo = localtime(¤tTime); - return asctime(timeinfo); -} - -// -// method: display -// description: display the elements of this timer control object -// -// @param none -// @return none -// -void TimerControl::display(void) -{ - string mapper[] = { "INIT", - "OFF", - "RUNNING", - "FINISHED", - "DISABLED" - }; - - printf("\r\n"); - std::string day_str; - if ( day == DAY_SCHEDULE_SUNDAY_MASK ) { - day_str = "Sun"; - } else if ( day == DAY_SCHEDULE_MONDAY_MASK ) { - day_str = "Mon"; - } else if ( day == DAY_SCHEDULE_TUESDAY_MASK ) { - day_str = "Tue"; - } else if ( day == DAY_SCHEDULE_WEDNESDAY_MASK ) { - day_str = "Wed"; - } else if ( day == DAY_SCHEDULE_THURSDAY_MASK ) { - day_str = "Thu"; - } else if ( day == DAY_SCHEDULE_FRIDAY_MASK ) { - day_str = "Fri"; - } else if ( day == DAY_SCHEDULE_SATURDAY_MASK ) { - day_str = "Sat"; - } - std::string week_str; - if ( week == WEEKLY_CHOICE_EVERY_WEEK ) { - week_str = "every"; - } else if ( week == WEEKLY_CHOICE_FIRST_WEEK ) { - week_str = "first"; - } else if ( week == WEEKLY_CHOICE_SECOND_WEEK ) { - week_str = "second"; - } else if ( week == WEEKLY_CHOICE_THIRD_WEEK ) { - week_str = "third"; - } else if ( week == WEEKLY_CHOICE_FOURTH_WEEK ) { - week_str = "fourth"; - } else if ( week == WEEKLY_CHOICE_LAST_WEEK ) { - week_str = "last"; - } else if ( week == WEEKLY_CHOICE_EVERYOTHER_WEEK ) { - week_str = "everyother"; - } - - char time_str[12]; - snprintf(time_str, sizeof(time_str), "%02d:%02d:%02d", startHour, startMin, startSec); - - cout << left << setw(8) << setfill(' ') << "timer:"; - cout << left << setw(32) << setfill(' ') << controlFile; - cout << left << setw(12) << setfill(' ') << id; - cout << "output: " << left << setw(16) << setfill(' ') << output; - cout << left << setw(6) << setfill(' ') << priority; - cout << "(" << setw(12) << week_str << ")" << day_str << "-> "; - cout << left << setw(12) << setfill(' ') << time_str; - cout << left << setw(10) << "duration: " << left << setw(6) << setfill(' ') << duration; - cout << left << setw(10) << setfill(' ') << mapper[currentState]; - if ( currentState == STATE_RUNNING ) { - cout << left << setw(11) << setfill(' ') << "ends in: " << (startTime + duration) - time(NULL) << " seconds "; - } else if ( currentState == STATE_FINISHED || currentState == STATE_DISABLED ) { - // show nothing - } else { - cout << left << setw(11) << setfill(' ') << "starts in: " << calcStartTime() << " seconds "; - } - cout.flush(); -}
--- a/ICE-Application/src/ConfigurationHandler/Controls/TimerControl.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -/****************************************************************************** - * - * File: TimerControl.h - * Desciption: ICE Timer Control Class - * - *****************************************************************************/ -#ifndef TIMERCONTROL_H -#define TIMERCONTROL_H - -//#include "ConfigurationHandler.h" -#include <string> -#include <stdio.h> -#include <vector> - -typedef enum { - TIMER_CONTROL_OK, - TIMER_CONTROL_UNK_STATE, - TIMER_CONTROL_ERROR, - TIMER_CONTROL_DESTROY -} TimerControlError_t; - - -//! Timer Control - implements a timer control. -/// @code -/// #include "TimerControl.h" -/// -/// int main(void) -/// { -/// TimerControl *t = new TimerControl(); -/// bool rc = t->load(controlFile); -/// if ( rc != true ) { -/// logError("%s: failed to load the manual control\n"); -/// delete t; -/// exit(-1); -/// } -/// -/// t->start(); -/// for ( ;; ) { -/// t->update(); -/// } -/// } -/// @endcode -class TimerControl -{ -private: - std::string controlFile; // file containing control data - std::string id; // timer identifier - std::string output; // output to control - unsigned int priority; // control priority - unsigned int day; - unsigned int startHour; // start hour (0-23) - unsigned int startMin; // start minute (0-59) - unsigned int startSec; // start second (0-59) - unsigned int duration; // duration in seconds - unsigned int week; // every week, first week, ... - - enum State { - STATE_INIT, - STATE_OFF, - STATE_RUNNING, - STATE_FINISHED, - STATE_DISABLED - }; - State currentState; - - unsigned long nextScheduledStartTime; - unsigned long startTime; - - bool isVirtualOutput; - - // validate control file data - bool validateControlData(const char *buf); - - // copy control file data to object - void copyControlData(const char *buf); - - // start a feed - void startFeed(void); - - // stop a feed - void stopFeed(void); - - // calculate time until start - unsigned long calcStartTime() const; - - const char *displayTime(void); - -public: - TimerControl() { - currentState = STATE_INIT; - } - ~TimerControl() { - printf("\r%s invoked\n", __func__); - } - /// load control data from a JSON configuration file - bool load(std::string filename); - - /// start a timer control instance - void start(void); - - /// update a timer control instance - TimerControlError_t update(void); - - /// unregister a timer control instance from the output thread - void unregisterControl(void); - - /// display pertinent data of a timer control instance - void display(void); - - std::string getControlFile(void) const { - return controlFile; - } - - std::string getId(void) const { - return id; - } - - std::string getOutput(void) const { - return output; - } - - unsigned int getPriority(void) const { - return priority; - } - - unsigned int getDuration(void) const { - return duration; - } - - State getCurrentState(void) const { - return currentState; - } -}; - -#endif
--- a/ICE-Application/src/ControlTask/ControlTask.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,228 +0,0 @@ -/****************************************************************************** - * - * File: ControlTask.cpp - * Desciption: source for the ICE Control task - * - *****************************************************************************/ - -#include "ControlTask.h" -#include <stdio.h> -#include "rtos.h" -#include "ConfigurationHandler.h" -#include "ICELog.h" -#include "global.h" - -// control functions -static void serviceManualControls(void); -static void serviceTimerControls(void); -static void serviceSetpointControls(void); -static void serviceCompositeControls(void); -static void serviceFailsafeControls(void); -static void serviceSequenceControls(void); - -static void sendMail(std::string controlFile, Action_t action, Control_t control); - -// -// Task: ControlTask -// Description: This task will loop through all of the configued control -// and run the updater -// -// @param[in] none -// @param[out] none -// @return none -// -void ControlTask(void const *args) -{ - printf("\r%s has started...\n", __func__); - - while ( true ) { - - serviceTimerControls(); - serviceManualControls(); - serviceSetpointControls(); - serviceCompositeControls(); - serviceFailsafeControls(); - serviceSequenceControls(); - } -} - -// -// function: serviceManualControls() -// description: run the updater on the manual controls -// -// @param[in] none -// @param[out] none -// @return none -// -static void serviceManualControls(void) -{ - ManualControlError_t rc; - - manual_mutex.lock(); - if ( !manualTable.empty() ) { - StringManualMap::iterator pos; - for ( pos = manualTable.begin(); pos != manualTable.end(); ++pos) { - rc = pos->second->update(); - if ( rc == MANUAL_CONTROL_DESTROY ) { - // send mail to the configuration handler to destroy this control - sendMail(pos->second->getControlFile(), ACTION_DESTROY, CONTROL_MANUAL); - } else if ( rc != MANUAL_CONTROL_OK ) { - logError("%s: error %d updating the manual control", __func__, rc); - } - } - } - manual_mutex.unlock(); -} - -// -// function: serviceSetpointControls -// description: run the updater on the setpoint controls -// -// @param[in] none -// @param[out] none -// @return none -// -static void serviceSetpointControls(void) -{ - SetpointControlError_t rc; - - setpoint_mutex.lock(); - if ( !setpointTable.empty() ) { - StringSetpointMap::const_iterator pos; - for ( pos = setpointTable.begin(); pos != setpointTable.end(); ++pos ) { - // run the updater - rc = pos->second->update(); - if ( rc != SETPOINT_CONTROL_OK ) { - logError("%s: error (%d) updating setpoint control %s", - __func__, rc, pos->second->getId().c_str()); - } - } - } - setpoint_mutex.unlock(); -} - -// -// function: serviceTimerControls -// description: run the updater on the timer controls -// -// @param[in] none -// @param[out] none -// @return none -// -static void serviceTimerControls(void) -{ - TimerControlError_t rc; - - timer_mutex.lock(); - if ( !timerTable.empty() ) { - StringTimerMap::const_iterator pos; - for ( pos = timerTable.begin(); pos != timerTable.end(); ++pos ) { - // run the updater - rc = pos->second->update(); - if ( rc != TIMER_CONTROL_OK ) { - logError("%s: error (%d) updating timer control %s", - __func__, rc, pos->second->getId().c_str()); - } - } - } - timer_mutex.unlock(); -} - -// -// function: serviceCompositeControls -// description: run the updater on the composite controls -// -// @param[in] none -// @param[out] none -// @return none -// -static void serviceCompositeControls(void) -{ - CompositeControlError_t rc; - - // service the composite controls - composite_mutex.lock(); - if ( !compositeTable.empty() ) { - StringCompositeMap::const_iterator pos; - for ( pos = compositeTable.begin(); pos != compositeTable.end(); ++pos ) { - // run the updater - rc = pos->second->update(); - if ( rc != COMPOSITE_CONTROL_OK ) { - logError("%s: error (%d) updating composite control %s", - __func__, rc, pos->second->getId().c_str()); - } - } - } - composite_mutex.unlock(); -} - -// -// function: serviceFailsafeControls -// description: run the updater on the failsafe controls -// -// @param[in] none -// @param[out] none -// @return none -// -static void serviceFailsafeControls(void) -{ - FailsafeControlError_t rc; - - // service the setpoint controls - failsafe_mutex.lock(); - if ( !failsafeTable.empty() ) { - StringFailsafeMap::const_iterator pos; - for ( pos = failsafeTable.begin(); pos != failsafeTable.end(); ++pos ) { - // run the updater - rc = pos->second->update(); - if ( rc != FAILSAFE_CONTROL_OK ) { - logError("%s: error (%d) updating failsafe control %s", - __func__, rc, pos->second->getId().c_str()); - } - } - } - failsafe_mutex.unlock(); -} - -static void serviceSequenceControls(void) -{ - SequenceControlError_t rc; - - // service the sequence controls - sequence_mutex.lock(); - if ( !sequenceTable.empty() ) { - StringSequenceMap::const_iterator pos; - for ( pos = sequenceTable.begin(); pos != sequenceTable.end(); ++pos ) { - rc = pos->second->run(); - if ( rc != SEQUENCE_CONTROL_OK ) { - logError("%s: error (%d) running sequence control %s", - __func__, rc, pos->second->getId().c_str()); - } - } - } - sequence_mutex.unlock(); -} - -// -// function: sendMail -// desctiption: send mail to the configuration handler -// -// @param[in] controlFile -> key -// @param[in] action -> destroy, create, modify, etc. -// @param[in] control -> manual, setpoint... -// @param[out] none -// @return none -// -static void sendMail(std::string controlFile, Action_t action, Control_t control) -{ - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = action; - msg->control = control; - strncpy(msg->controlFile, controlFile.c_str(), sizeof(msg->controlFile)-1); - - printf("%s: Sending a destroy request for control %s\r\n", - __func__, msg->controlFile); - - ConfigHandlerMailBox.put(msg); -} \ No newline at end of file
--- a/ICE-Application/src/ControlTask/ControlTask.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -#ifndef CONTROLTASK_H -#define CONTROLTASK_H - -#ifdef __cplusplus -#define EXTERNC extern "C" -#else -#define EXTERNC -#endif - -EXTERNC void ControlTask(void const *args); - -#undef EXTERNC - -#endif
--- a/ICE-Application/src/DataHandler/DataHandler.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -#include "DataHandler.h" -#include "rtos.h" -#include <stdio.h> - -void DataHandler(void const *args) -{ - printf("\r%s has started...\n", __func__); - - while ( true ) { - Thread::wait(5000); - } -}
--- a/ICE-Application/src/DataHandler/DataHandler.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -#ifndef DATAHANDLER_H -#define DATAHANDLER_H - -#ifdef __cplusplus -#define EXTERNC extern "C" -#else -#define EXTERNC -#endif - -EXTERNC void DataHandler(const void *args); - -#undef EXTERNC - -#ifdef MDOT_ICE -#include "mDot.h" -#endif -#include <string> - -#define HEARTBEAT_MSG_MTYPE 20 // heartbeat (out) - -#define SETPOINT_CONTROL_MTYPE 100 // setpoint control config -#define TIMER_CONTROL_MTYPE 101 // timer control config -#define PID_CONTROL_MTYPE 102 // PID control config -#define MANUAL_CONTROL_MTYPE 103 // manual control config -#define COMPOSITE_CONTROL_MTYPE 104 // composite control config -#define SEQUENCE_CONTROL_MTYPE 105 // sequence control -#define FAILSAFE_CONTROL_MTYPE 106 // failsafe conrol - -#define INPUT_CONFIG_MTYPE 200 // input config -#define OUTPUT_CONFIG_MTYPE 201 // output config -#define VINPUT_CONFIG_MTYPE 203 // virtual input config -#define VOUTPUT_CONFIG_MTYPE 204 // virtual output config -#define HOLDING_CONFIG_MTYPE 205 // holding config - -#define VIRTUAL_COMMAND_MTYPE 250 // virtual command -#define EQUATION_COMMAND_MTYPE 251 // equation command - -#define DESTROY_SETPOINT_MTYPE 300 // destroys a setpoint control -#define DESTROY_TIMER_MTYPE 301 // destroys a timer control -#define DESTROY_PID_MTYPE 302 // destroys a PID control -#define DESTROY_MANUAL_MTYPE 303 // destroys a manual control - -#define EVENT_LOG_MTYPE 300 // event log [out], pump actuation, etc. -#define DEVICE_CONN_MTYPE 301 // device connected -#define LIVE_DATA_MTYPE 400 // live data [out] - - -// BLE requests -#define BT_MODBUS_HOLD_COMMAND_MTYPE 1000 -#define BT_MODBUS_RAW_COMMAND_MTYPE 1002 -#define BT_MODBUS_COMMAND_REPLY_MTYPE 1001 - -#define BT_GETLOG_COMMAND_MTYPE 1100 -#define BT_GETLOG_COMMAND_REPLY_MTYPE 1101 -#define BT_GETLIVE_COMMAND_MTYPE 1200 - -#define BT_START_CAL_COMMAND_MTYPE 1300 -#define BT_1PT_CAL_COMMAND_MTYPE 1301 - -#define SETPOINT_CONTROL_REPLY_MTYPE 500 -#define TIMER_CONTROL_REPLY_MTYPE 501 -#define MANUAL_CONTROL_REPLY_MTYPE 503 -#define COMPOSITE_CONTROL_REPLY_MTYPE 504 -#define SEQUENCE_CONTROL_REPLY_MTYPE 505 -#define FAILSAFE_CONTROL_REPLY_MTYPE 506 - -#define INPUT_CONFIG_REPLY_MTYPE 550 -#define OUTPUT_CONFIG_REPLY_MTYPE 551 - -#ifdef MDOT_ICE -bool StoreReceivedFile( mDot *dot, std::string &payload_string ); -#endif - -#endif \ No newline at end of file
--- a/ICE-Application/src/DataHandler/LogHandler.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +0,0 @@ -#include "global.h" -#include <stdio.h> -#include <sstream> -#include <iostream> -#include <time.h> -#include "LogHandler.h" -#include "LogLocalApi.h" -#include "LoggerApi.h" -#ifdef MDOT_ICE -#include "BLEDataHandler.h" -#include "MTSLog.h" -#include "CloudFileReceiver.h" -#include "CloudDataHandler.h" -#endif - -#ifndef MDOT_ICE -#include "DataHandler.h" -#endif -#include "utilities.h" -#include "version.h" - -Mail<LoggerQueue_t, 16> LoggerQueue; - -size_t LoggerXmitLength = LOG_BYTES_PER_ENTRY; - -int last_min=8; - - -static void constructHeartBeat(std::string &msg); - -// -// function: LogHandler -// description: -// -// @param[in] joined -// @param[out] none -// @return -// -bool LogHandler( bool joined ) -{ - int32_t ret; - char buffer[LOG_BYTES_PER_ENTRY]; - std::string tmp_buffer; - bool log_in_eeprom=false; - bool log_to_send=false; - -#ifdef MDOT_ICE - osEvent evt = LoggerQueue.get(50); - if( (evt.status == osEventMail) ) { - // pull the log event from the queue, even if we are not joined - LoggerQueue_t *LoggerEvent = (LoggerQueue_t*)evt.value.p; - logInfo("Log Msg Received: log entry: %s", LoggerEvent->log_entry); - strncpy( buffer, LoggerEvent->log_entry, (LoggerXmitLength-1) ); - tmp_buffer.assign(buffer); - log_to_send = true; -// printf("%s:%d: Found Log Event on LoggerQueue to send to cloud\r\n", __func__, __LINE__); - LoggerQueue.free(LoggerEvent); - } - - if( (evt.status == osEventMail) && (joined == false) ) { - // if we pulled a log from the queue put it in the EEPROM - LogLocalApi( tmp_buffer.c_str() ); -// printf("%s:%d: Not Connected, Putting Logger event In EEPROM\r\n", __func__, __LINE__); - return false; - } - - if( (evt.status != osEventMail) && joined == true) { - // nothing on the queue, see if there is anything in the EEPROM to send. - log_in_eeprom = LogLocalApi_PopEntry( buffer ); - if( log_in_eeprom == true ) { - tmp_buffer.assign(buffer); - log_to_send = true; -// printf("%s:%d: Found Log Event in EEPROM to send to cloud\r\n", __func__, __LINE__); - } else { -// printf("%s:%d: Nothing in EEPROM\r\n", __func__, __LINE__); - } - } - - if( log_to_send == false ) { - struct tm *ts; - time_t curr_sec; - - curr_sec = time(0); - ts = localtime(&curr_sec); -// printf("curr_sec=%ld, min=%d (last=%d) min-mod-5=%d\r\n", curr_sec, ts->tm_min, last_min, (ts->tm_min%5) ); - if( ((ts->tm_min%1) == 0) && (ts->tm_min != last_min) ) { - last_min = ts->tm_min; - // no event log to send, send the heart beat message. - logInfo("\r%s:%d: sending heart beat, hr=%d min=%d\r\n", __func__, __LINE__, ts->tm_hour, ts->tm_min); - constructHeartBeat(tmp_buffer); - logInfo("\r%s (%d bytes)\n", tmp_buffer.c_str(), tmp_buffer.size()); - } else { -// printf("%s:%d: Nothing to send to the cloud\r\n", __func__, __LINE__); - if( CloudDataHandler_RcvFile != true ) { - return false; - } - printf("%s:%d: currently receiveing a file\r\n", __func__, __LINE__); - // send so we get another packet from gateway. - tmp_buffer = "{\"mtype\":\"20\"}"; - } - } - - if( joined == true ) { - std::vector<uint8_t> data(tmp_buffer.begin(), tmp_buffer.end()); - ret = GLOBAL_mdot->send(data); - if ( ret == mDot::MDOT_OK ) { - //if ((ret = GLOBAL_mdot->send(data)) == mDot::MDOT_OK) { -// printf("%s:%d: Successful send to cloud\r\n", __func__, __LINE__); - return true; - } -// printf("failed to send, ret=%d, %s\r\n", ret, mDot::getReturnCodeString(ret).c_str()); - } - - if( log_to_send == true ) { - // We had a log event ready to send but didn't send it. - // Store it in the EEPROM for the next attempt. -// printf("%s:%d: Could not send Log Event to cloud, store in EEPROM\r\n", __func__, __LINE__); - LogLocalApi( tmp_buffer.c_str() ); - } else { -// printf("%s:%d: Could not send to cloud, nothing to store in EEPROM\r\n", __func__, __LINE__); - } -#endif - return false; -} - -// -// function: CDH_ReplyToHandler -// description: -// -// @param[in] -// @param[out] -// @return -// -void CDH_ReplyToHandler( std::string &id, int status, float value ) -{ - std::ostringstream reply_oss; - - LoggerQueue_t *LoggerEvent = LoggerQueue.alloc(); - memset( LoggerEvent->log_entry, 0, sizeof(LoggerEvent->log_entry) ); - - reply_oss << "{ \"mtype\":" << BT_MODBUS_COMMAND_REPLY_MTYPE << ", \"mbreply\":{ \"id\":\"" << id.c_str() << "\"," "\"status\":\"" << status << "\"," "\"value\":\"" << value << "\"} }"; - std::string string_reply = reply_oss.str(); - strncpy( LoggerEvent->log_entry, string_reply.c_str(), (sizeof(LoggerEvent->log_entry)-1)); - printf("%s:%d: Reply is: %s\r\n",__func__,__LINE__, LoggerEvent->log_entry); - LoggerEvent->position = 0; - LoggerQueue.put(LoggerEvent); -} - -// -// function: ReplyToHandler -// description: -// -// @param[in] -// @param[out] -// @return -// -void ReplyToHandler( ThreadName_t replyTo, std::string &id, int ret, float value ) -{ -#ifdef MDOT_ICE - if( replyTo == BLE_HANDLER ) { - BLE_ReplyToHandler( id, ret, value ); - } else if( replyTo == CLOUD_DATA_HANDLER ) { - CDH_ReplyToHandler( id, ret, value ); - } else { - logError("Unknown Thread for Command Reply"); - } -#else -#undef TODO_ICE - // do something -#endif -} - -// -// function: constructHeartBeat() -// description: construct the periodic heartbeat message -// -// @param[out] constructed message -// @return none -// -static void constructHeartBeat(std::string &msg) -{ - //std::ostringstream heartbeat_msg(200); - std::ostringstream heartbeat_msg; - heartbeat_msg.precision(2); - - std::string heap_data = Util_getHeapData(); - // let's strip off everything after 'bytes' - std::string::size_type i = heap_data.find("bytes"); - if (i != std::string::npos) - heap_data.erase(i+strlen("bytes"), heap_data.length()); - - - heartbeat_msg << "{ \"mtype\":" << HEARTBEAT_MSG_MTYPE << - ", \"hb\": {" - "\"fwver\": \"" << MAJOR_VERSION_NUMBER << "." << MINOR_VERSION_NUMBER << "." << PATCH_VERSION_NUMBER << "\"," - "\"heap\":\"" << heap_data << "\"," - "\"AL\":\"" << (((double)GLOBAL_analyticsLogger_thread->max_stack()/(double)GLOBAL_analyticsLogger_thread->stack_size())*100.) << "%\"," - "\"DH\":\"" << (((double)GLOBAL_dataHandler_thread->max_stack()/(double)GLOBAL_dataHandler_thread->stack_size())*100.) << "%\"," - "\"CFG\":\"" << (((double)GLOBAL_configHandler_thread->max_stack()/(double)GLOBAL_configHandler_thread->stack_size())*100.) << "%\"," - "\"CT\":\"" << (((double)GLOBAL_controlTask_thread->max_stack()/(double)GLOBAL_controlTask_thread->stack_size())*100.) << "%\"," - "\"MM\":\"" << (((double)GLOBAL_modbusMaster_thread->max_stack()/(double)GLOBAL_modbusMaster_thread->stack_size())*100.) << "%\"," - "\"OUT\":\"" << (((double)GLOBAL_outputTask_thread->max_stack()/(double)GLOBAL_outputTask_thread->stack_size())*100.) << "%\"," - "\"boot\":\"" << Util_getLastBootTime() << "\" " - "} }"; - msg = heartbeat_msg.str(); -}
--- a/ICE-Application/src/DataHandler/LogHandler.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -#ifndef LOGHANDLER_H -#define LOGHANDLER_H - -#include "global.h" -#include "LogLocalApi.h" - -typedef struct logger_queue_t { - size_t position; - char log_entry[LOG_BYTES_PER_ENTRY]; -} LoggerQueue_t; - -extern Mail<LoggerQueue_t, 16> LoggerQueue; - -bool LogHandler( bool joined ); - -#endif -
--- a/ICE-Application/src/DataHandler/LoggerApi.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,152 +0,0 @@ -#include "LoggerApi.h" -#include "LogHandler.h" -#include "DataHandler.h" -#include "ICELog.h" -#include "global.h" -#include <sstream> -#include <iostream> -#include <time.h> -#include <stdio.h> - - -void EventLoggerApi( EventReasonStruct_t &eventReason ) -{ - time_t curr_sec = time(0); - - LoggerQueue_t *LoggerEvent = LoggerQueue.alloc(); - memset( LoggerEvent->log_entry, 0, sizeof(LoggerEvent->log_entry) ); - -// std::ostringstream log_event; -// log_event << "tag:"<< iter->first.c_str() << " value:" << float_value; - - snprintf( LoggerEvent->log_entry, sizeof(LoggerEvent->log_entry), - "{""\"mtype\":\"%d\",""\"t\":\"%ld\",""\"e\":\"%d\",""\"i\":\"%s\",""\"iv\":\"%2.2f\",""\"o\":\"%s\",""\"ov\":\"%2.2f\"}", - EVENT_LOG_MTYPE, curr_sec, eventReason.eventReason, eventReason.inputTag, eventReason.inputValue, - eventReason.outputTag, eventReason.outputValue ); - LoggerEvent->position = 0; - LoggerQueue.put(LoggerEvent); - - logInfo("len=%d: %s", strlen(LoggerEvent->log_entry), LoggerEvent->log_entry ); - - PeriodicLogger( true ); -} - -void DeviceConnectedLoggerApi( unsigned int connected ) -{ - time_t curr_sec = time(NULL); - LoggerQueue_t *LoggerEvent = LoggerQueue.alloc(); - - memset( LoggerEvent->log_entry, 0, sizeof(LoggerEvent->log_entry) ); - - snprintf( LoggerEvent->log_entry, sizeof(LoggerEvent->log_entry), - "{""\"mtype\":\"%d\",""\"t\":\"%ld\",""\"e\":\"%u\" }", - DEVICE_CONN_MTYPE, curr_sec, connected); - LoggerEvent->position = 0; - LoggerQueue.put(LoggerEvent); - - logInfo("len=%d: %s", strlen(LoggerEvent->log_entry), LoggerEvent->log_entry); - printf("\rlen = %d: %s\n", strlen(LoggerEvent->log_entry), LoggerEvent->log_entry); - - PeriodicLogger( true ); -} - - -void LiveData2Message( const char *liveData, char *messageString, int len ) -{ - time_t curr_sec = time(0); - memset( messageString, 0, len ); - snprintf( messageString, len, "{""\"mtype\":\"%d\",""\"t\":\"%ld\",%s}", LIVE_DATA_MTYPE, curr_sec, liveData ); - logInfo("%s:%d: packetLen=%d: %s", __func__,__LINE__,strlen(messageString), messageString ); -} - -void LiveDataLoggerApi( const char *liveData ) -{ - time_t curr_sec = time(0); - - LoggerQueue_t *LoggerEvent = LoggerQueue.alloc(); - - LiveData2Message( liveData, LoggerEvent->log_entry, sizeof(LoggerEvent->log_entry) ); - - LoggerEvent->position = 0; - LoggerQueue.put(LoggerEvent); - - logInfo("dataLen=%d, packetLen=%d: %s", strlen(liveData), strlen(LoggerEvent->log_entry), LoggerEvent->log_entry ); -} - -void PeriodicLogger( bool force ) -{ - bool log_sent=false; - std::ostringstream log_event; - - int map_count = ModbusRegisterMap.size(); - int collected_count = 0; - - log_event << "\"lr\":["; - std::map<std::string, ModbusRegister>::iterator iter; - for (iter = ModbusRegisterMap.begin(); iter != ModbusRegisterMap.end(); ++iter) { - - collected_count = collected_count + 1; - - log_event << "{\"t\":"<< "\"" << iter->first.c_str() << "\"," << "\"v\":"<< "\"" << RegisterValueMap[iter->first].float_value<< "\"},"; - log_sent = false; - if( log_event.str().size() >= 150 ) { - std::string str = log_event.str(); - str.erase( str.size() - 1 ); - if( collected_count == map_count ) { - str.append("],\"seq\":\"0\""); - } else { - str.append("],\"seq\":\"1\""); - } -// printf("%s:%d: Logging %s : len=%d\r\n", __func__, __LINE__, str.c_str(), str.length() ); - LiveDataLoggerApi( str.c_str() ); - log_event.str(""); - log_event.clear(); - log_event << "\"lr\":["; - log_sent = true; - } - } - if( log_sent == false ) { - std::string str = log_event.str(); - str.erase( str.size() - 1 ); - str.append("],\"seq\":\"0\""); - if( str.length() > 20 ) { -// printf("%s:%d: Logging %s : len=%d\r\n", __func__, __LINE__, str.c_str(), str.length() ); - LiveDataLoggerApi( str.c_str() ); - } - } -} - -void GetCurrentReadings( char *currentReadings, int len ) -{ - //bool log_sent=false; - std::ostringstream log_event; - - log_event << "\"lr\":["; - std::map<std::string, ModbusRegister>::iterator iter; - for (iter = ModbusRegisterMap.begin(); iter != ModbusRegisterMap.end(); ++iter) { - log_event << "{\"t\":"<< "\"" << iter->first.c_str() << "\"," << "\"v\":"<< "\"" << RegisterValueMap[iter->first].float_value<< "\"},"; - } - - std::string str = log_event.str(); - str.erase( str.size() - 1 ); - str.append("],\"seq\":\"0\""); - LiveData2Message( str.c_str(), currentReadings, len ); -// printf("GetCurrentReadings: packetLen=%d: %s, str.size(%d), (%s)\r\n", strlen(currentReadings), currentReadings, str.size(), str.c_str() ); -} - -void GetTagReadings( std::vector<std::string> RequestedTags, char *currentReadings, int len ) -{ - std::ostringstream log_event; - - log_event << "\"lr\":["; - std::vector<std::string>::iterator iter; - for (iter = RequestedTags.begin(); iter != RequestedTags.end(); ++iter) { - log_event << "{\"t\":"<< "\"" << iter->c_str() << "\"," << "\"v\":"<< "\"" << RegisterValueMap[*iter].float_value<< "\"},"; - } - - std::string str = log_event.str(); - str.erase( str.size() - 1 ); - str.append("]"); - LiveData2Message( str.c_str(), currentReadings, len ); -// printf("GetCurrentReadings: packetLen=%d: %s, str.size(%d), (%s)\r\n", strlen(currentReadings), currentReadings, str.size(), str.c_str() ); -} \ No newline at end of file
--- a/ICE-Application/src/DataHandler/LoggerApi.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -#ifndef LOGGERAPI_H -#define LOGGERAPI_H - -#include "global.h" - -#include <vector> -#include <string> - -extern void EventLoggerApi( EventReasonStruct_t &eventReason ); -extern void DeviceConnectedLoggerApi( unsigned int connected ); -extern void LiveDataLoggerApi( const char *liveData ); -extern void PeriodicLogger( bool force ); -extern void LiveData2Message( const char *liveData, char *messageString, int len ); -extern void GetCurrentReadings( char *currentReadings, int len ); -void GetTagReadings( std::vector<std::string> RequestedTags, char *currentReadings, int len ); -void ReplyToHandler( ThreadName_t replyTo, std::string &id, int ret, float value ); -void CDH_ReplyToHandler( std::string &id, int status, float value ); - -#endif -
--- a/ICE-Application/src/Drivers/LogLocalApi.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -#include <stdio.h> - -#include "LogLocalApi.h" -#ifdef MDOT_ICE -#include "MTSLog.h" -#endif -#include "global.h" - -#define WAIT_TIME 5 - - -int LocalLogEntries = 0; - -void LogLocalApi( const char *logString ) -{ - LogLocalApi_PushEntry( logString ); -} - -void LogLocalApi_PushEntry( const char *logString ) -{ - -} - -bool LogLocalApi_PopEntry( char *logString ) -{ - return true; -} -
--- a/ICE-Application/src/Drivers/LogLocalApi.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -#ifndef LOGLOCALAPI_H -#define LOGLOCALAPI_H - -#include <string.h> - -#define LOG_BYTES_PER_ENTRY 256 -#define LOG_END_OF_STORAGE (EEP_NBYT - LOG_BYTES_PER_ENTRY) - -void LogLocalApi( const char *logString ); -void LogLocalApi_PushEntry( const char *logString ); -bool LogLocalApi_PopEntry( char *logString ); - -#endif -
--- a/ICE-Application/src/Drivers/i2c_eeprom.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -/** @file i2c_eeprom.cpp */ -/*CPP************************************************************************** - * FILENAME : i2c_eeprom.cpp * - * * - * DESCRIPTION : * - * Simple library for external I2C EEEPROM. * - * * - * AUTHOR : Olli Vanhoja START DATE : 2011-02-17 * - *****************************************************************************/ - -#include "mbed.h" -#include "i2c_eeprom.h" -#include "global.h" - -I2C i2c_instance(I2C_SDA, I2C_SCL); - -i2c_eeprom::i2c_eeprom(int hwAddr, int speed) -{ - i_i2c_address = hwAddr; -// i2c->frequency(speed); -} - -void i2c_eeprom::write(char *data, uint16_t iAddr, unsigned int n) -{ - char *pi2c_data[3]; // Pointers for CW items - char i2c_data[3]; // Final CW - - /* Convert address to hi and low byte array - * This is really pointless even though they are - * called pointers it would be lot easier to do this - * conversion without any pointers */ - uint16_t *piAddr = &iAddr; - pi2c_data[0] = (char *)piAddr+1; - pi2c_data[1] = (char *)piAddr; - - for (uint16_t i=0; i < n; i++) - { - pi2c_data[2] = &data[i]; - - // Apply actual values from pointer - //for (int n=0; n < 3; n++) - // i2c_data[n] = *pi2c_data[n]; - i2c_data[0] = *pi2c_data[0]; - i2c_data[1] = *pi2c_data[1]; - i2c_data[2] = *pi2c_data[2]; - - // Send write command - strwrite: -// printf("(%s:%d): iAddr=0x%x, data=0x%x\r\n", __func__, __LINE__, iAddr, data[0]); - if(i2c->write(i_i2c_address, i2c_data, 3)) - { - autoreset(); - goto strwrite; - } - - iAddr++; // increment address counter - - // Wait for ACK - while(i2c->write(i_i2c_address, NULL, 0)){} - } -} - -void i2c_eeprom::read(uint16_t iAddr, uint16_t n, char *out) -{ - char *pi2c_data[2]; // Pointers for CW items - char i2c_data[2]; // Final CW - - uint16_t *piAddr = &iAddr; - pi2c_data[0] = (char *)piAddr+1; - pi2c_data[1] = (char *)piAddr; - - // Apply actual values from pointer - //for (int i=0; i < 2; i++) - // i2c_data[i] = *pi2c_data[i]; - i2c_data[0] = *pi2c_data[0]; - i2c_data[1] = *pi2c_data[1]; - - // Send read command - strread: - if(i2c->write(i_i2c_address, i2c_data, 2)) - { - autoreset(); - goto strread; - } - if(i2c->read(i_i2c_address, out, n)) - { - autoreset(); - goto strread; - } -// printf("(%s:%d): iAddr=0x%x, data=0x%x\r\n", __func__, __LINE__, iAddr, *out); -} - -void i2c_eeprom::autoreset() -{ -} -
--- a/ICE-Application/src/Drivers/i2c_eeprom.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/*H**************************************************************************** - * FILENAME : i2c_eeprom.h * - * * - * DESCRIPTION : * - * Simple library for external I2C EEEPROM. * - * * - * AUTHOR : Olli Vanhoja START DATE : 2011-02-17 * - ****************************************************************************** - * - * CHANGES : - * - * VERSION DATE WHO DETAIL - * 0.1 2011-02-21 Olli Vanhoja Initial release version - * 0.2 2011-02-21 Olli Vanhoja *Added possibility change I2C speed - * *Added external reset pin and autoreset - * for read function. Thanks to Jon Ward. - * *Documentational comments added. - * 0.3 2011-02-21 Olli Vanhoja *Auto-reset for all error conditions - * - *H*/ - -#ifndef I2C_EEPROM_H -#define I2C_EEPROM_H - -/** I2C EEPROM access class - * - * This class is used for communication with I2C EEPROM chip. - */ -class i2c_eeprom { -private: - int i_i2c_address; // I2C harware address - void autoreset(); -public: - /** Initialize communication - * - * @param hwAddr Harware address of the I2C EEPROM chip. - * @param speed I2C bus speed. - */ - i2c_eeprom(int hwAddr, int speed); - - /** Write to I2C EEPROM - * - * Write any length of bytes to external EEPROM. - * - * @param *data Array of bytes. - * @param iAddr Memory address. - * @param n Write n bytes. - */ - void write(char *data, uint16_t iAddr, unsigned int n); - - /** Read from I2C EEPROM - * - * Read any length of bytes from external EEPROM. - * - * @param iAddr Memory address. - * @param n Read n bytes. - * @param *out Returns array of bytes. - */ - void read(uint16_t iAddr, uint16_t n, char *out); -}; - -#endif
--- a/ICE-Application/src/Drivers/mod.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,516 +0,0 @@ -// modbus -// --------------------------------------------------------------------------------------------------------------------- -// includes - -#include "mbed.h" -#include "global.h" -#include "mod.h" - -// --------------------------------------------------------------------------------------------------------------------- -// globals - -unsigned short int mod_data[MOD_DATA]; -unsigned short int mod_ireg[MOD_IREG]; - -int mod_verbose = 0; - -// ------------------------------------------------------------------------------------------------- -// modbus buffers - -#define MOD_RXNUM 200 -#define MOD_TXNUM 200 - -int mod_rxnum = 0; -int mod_txnum = 0; - -unsigned char mod_rxbuf[MOD_RXNUM]; -unsigned char mod_txbuf[MOD_TXNUM]; - -// ------------------------------------------------------------------------------------------------- -// print functions - -void print_uc(char hex, char bin, char dec, char* pre, unsigned char input, char* post) -{ - if(pre[0] != 0) printf("%s", pre); - - if(hex) - { - printf("0x%02X", input); - } - if(bin) - { - if(hex) printf(" "); - for(int i=7; i>=0; i--) - { - if(i<7 && (i%4)==3) printf(" "); if(input & (1<<i)) printf("1"); else printf("0"); - } - } - if(dec) - { - if(hex || bin) printf(" "); printf("%u", input); - } - if(post[0] != 0) printf("%s", post); -} - - -void print_us(char hex, char bin, char dec, char* pre, unsigned short int input, char* post) -{ - if(pre[0] != 0) printf("%s", pre); - - if(hex) - { - printf("0x%04X", input); - } - if(bin) - { - if(hex) printf(" "); - for(int i=15; i>=0; i--) - { - if(i<15 && (i%4)==3) printf(" "); if(input & (1<<i)) printf("1"); else printf("0"); - } - } - if(dec) - { - if(hex || bin) printf(" "); printf("%u", input); - } - if(post[0] != 0) printf("%s", post); -} - -// ------------------------------------------------------------------------------------------------- -// initialization - -RawSerial mod(PC_6, PC_7); - -DigitalOut mod_de(PC_8); -DigitalOut mod_re(PC_9); - -void mod_init(void) -{ - mod_de = 0; - mod_re = 0; - - mod.baud(MOD_BAUD); - mod.attach(&mod_process, RawSerial::RxIrq); - - for(int i=0; i<MOD_IREG; i++) mod_ireg[i] = 0; -} - -// ------------------------------------------------------------------------------------------------- -// utilities - -void mod_clear(void) -{ - mod_rxnum = 0; for(int i=0; i<MOD_RXNUM; i++) mod_rxbuf[i] = 0; - mod_txnum = 0; for(int i=0; i<MOD_TXNUM; i++) mod_txbuf[i] = 0; -} - -unsigned short int mod_calc_crc(unsigned char *buffer, int length) -{ - unsigned short int temp=0xFFFF, flag; - - for(int i=0; i<length; i++) - { - temp = temp ^ buffer[i]; - - for(int j=1; j<=8; j++) - { - flag = temp & 0x0001; - temp = temp >> 1; - if(flag) temp = temp ^ 0xA001; - } - } - - return(temp); -} - -void mod_ireg_dump(void) -{ - for(int i=0; i<MOD_IREG; i++) - { - printf(" I%02d = ", i); - print_us(1,0,1, "", mod_ireg[i], "\r\n"); - } -} - -// --------------------------------------------------------------------------------------------------------------------- -// process received data - -void mod_process(void) -{ - if(mod.readable()) - { - if(mod_rxnum < MOD_RXNUM) - { - mod_rxbuf[mod_rxnum++] = mod.getc(); - } - else - { - if(mod_verbose) printf(" [mod_process] buffer full\r\n"); - mod_clear(); - } - } -} - -// --------------------------------------------------------------------------------------------------------------------- -// MOD_FUNC_GET_IREG (NBYT = total bytes of data transmitted) -// MOD_FUNC_GET_HREG -// -// byte: 0 1 2 3 4 5 6 7 -// received: ADDR FUNC SREG-H SREG-L NREG-H NREG-L RCRC-L RCRC-H -// transmit: ADDR FUNC NBYT DATA-H DATA-L TCRC-L TCRC-H NULL - -int mod_read(int inp_addr, int inp_func, int inp_sreg, int inp_nreg, unsigned char *rd_buf) -{ - int mod_error = 0; - - unsigned char tx_addr = inp_addr; - unsigned char tx_func = inp_func; - WORD2BYTE tx_sreg; tx_sreg.value = inp_sreg; - if(inp_nreg < 1) inp_nreg = 1; - WORD2BYTE tx_nreg; tx_nreg.value = inp_nreg; - -// fill transmit buffer - - mod_clear(); - - mod_txbuf[mod_txnum++] = tx_addr; - mod_txbuf[mod_txnum++] = tx_func; - mod_txbuf[mod_txnum++] = tx_sreg.byte1; - mod_txbuf[mod_txnum++] = tx_sreg.byte0; - mod_txbuf[mod_txnum++] = tx_nreg.byte1; - mod_txbuf[mod_txnum++] = tx_nreg.byte0; - - WORD2BYTE tx_crc; tx_crc.value = mod_calc_crc(mod_txbuf, mod_txnum); - mod_txbuf[mod_txnum++] = tx_crc.byte0; - mod_txbuf[mod_txnum++] = tx_crc.byte1; - -// transmit - - mod_de = 1; - mod_re = 1; - wait_ms(1); - for(int i=0; i<mod_txnum; i++) mod.putc(mod_txbuf[i]); - wait_ms(2); - mod_de = 0; - mod_re = 0; - - wait_ms(30); - -// output transmit buffer - - if(mod_verbose) - { - printf(" mod_txbuf: %d\r\n\r\n", mod_txnum); - for(int i=0; i<mod_txnum; i++) - { - printf(" %02d", i); print_uc(1,0,1, " = ", mod_txbuf[i], "\r\n"); - } - if(mod_txnum) - { - printf("\r\n"); - print_uc(1,0,1, " addr = ", tx_addr, "\r\n"); - print_uc(1,0,1, " func = ", tx_func, "\r\n"); - print_us(1,0,1, " sreg = ", tx_sreg.value, "\r\n"); - print_us(1,0,1, " nreg = ", tx_nreg.value, "\r\n"); - print_us(1,0,1, " tcrc = ", tx_crc.value, "\r\n"); - } - } - -// process received buffer - - if(mod_verbose) - { - printf("\r\n mod_rxbuf: %d\r\n\r\n", mod_rxnum); - for(int i=0; i<mod_rxnum; i++) - { - printf(" %02d", i); print_uc(1,0,1, " = ", mod_rxbuf[i], "\r\n"); - } - } - if(mod_rxnum) - { - int rxnum = 0; - unsigned char rx_addr = mod_rxbuf[rxnum++]; - unsigned char rx_func = mod_rxbuf[rxnum++]; - - if(mod_verbose) - { - print_uc(1,0,1, "\r\n addr = ", rx_addr, "\r\n"); - print_uc(1,0,1, " func = ", rx_func, ""); - } - if(rx_func == tx_func) - { - int rd_buf_index = 0; - unsigned char rx_nbyt = mod_rxbuf[rxnum++]; - - if(mod_verbose) print_uc(1,0,1, "\r\n nbyt = ", rx_nbyt, "\r\n\r\n"); - - for(int i=0; i<rx_nbyt/2; i++) - { - WORD2BYTE rx_data; - rd_buf[rd_buf_index++] = rx_data.byte1 = mod_rxbuf[rxnum++]; - rd_buf[rd_buf_index++] = rx_data.byte0 = mod_rxbuf[rxnum++]; - - int ireg = tx_sreg.value + i; - - if(ireg < MOD_IREG) mod_ireg[ireg] = rx_data.value; - - if(mod_verbose) - { - printf(" data(%03d)", ireg); - print_us(1,0,1, " = ", rx_data.value, "\r\n"); - } - } - WORD2BYTE rx_crc; - rx_crc.byte0 = mod_rxbuf[rxnum++]; - rx_crc.byte1 = mod_rxbuf[rxnum++]; - - if(mod_verbose) print_us(1,0,1, "\r\n rcrc = ", rx_crc.value, "\r\n"); - } - else - { - unsigned char rx_nerr = mod_rxbuf[rxnum++]; mod_error = rx_nerr; - - if(mod_verbose) - { - if(rx_func == tx_func+0x80) printf(" (func %d error)\r\n", tx_func); - else printf(" (unknown func error)\r\n"); - print_uc(1,0,1, " nerr = ", rx_nerr, " ("); - - switch(rx_nerr) - { - case MOD_ERROR_ILL_FUNC: ; printf("slave received illegal function code") ; break; - case MOD_ERROR_ILL_ADDR: ; printf("slave received illegal register address") ; break; - case MOD_ERROR_ILL_DATA: ; printf("slave received illegal register value") ; break; - case MOD_ERROR_SLAVE_FAIL: ; printf("slave device-specific failure") ; break; - case MOD_ERROR_SLAVE_ACK: ; printf("slave ACK") ; break; - case MOD_ERROR_SLAVE_BUSY: ; printf("slave is busy") ; break; - case MOD_ERROR_SLAVE_NACK: ; printf("slave NACK") ; break; - case MOD_ERROR_SLAVE_PARITY: ; printf("slave memory parity error") ; break; - case MOD_ERROR_RX_TIMEOUT: ; printf("master receive timeout") ; break; - case MOD_ERROR_TX_TIMEOUT: ; printf("master transmit timeout") ; break; - case MOD_ERROR_CRC: ; printf("master CRC error") ; break; - default: ; printf("unknown error") ; break; - } - printf(")\r\n"); - } - - WORD2BYTE rx_crc; - rx_crc.byte0 = mod_rxbuf[rxnum++]; - rx_crc.byte1 = mod_rxbuf[rxnum++]; - - if(mod_verbose) print_us(1,0,1, " rcrc = ", rx_crc.value, "\r\n"); - } - } - else - { - mod_error = MOD_ERROR_RX_TIMEOUT; // no received data - } - - if(mod_verbose) printf("\r\n mod_error: %d\r\n", mod_error); - - return(mod_error); -} - -// --------------------------------------------------------------------------------------------------------------------- -// MOD_FUNC_SET_HREG -// -// byte: 0 1 2 3 4 5 6 7 8 -// received: ADDR FUNC SREG-H SREG-L DATA-H DATA-L RCRC-L RCRC-H -// transmit: ADDR FUNC SREG-H SREG-L DATA-H DATA-L TCRC-L TCRC-H NULL -// -// MOD_FUNC_SET_HREGS -// -// byte: 0 1 2 3 4 5 6 7 8 9 10 -// received: ADDR FUNC SREG-H SREG-L NREG-H NREG-L NBYT DATA-H DATA-L RCRC-L RCRC-H -// transmit: ADDR FUNC SREG-H SREG-L NREG-H NREG-L TCRC-L TCRC-H NULL -// -int mod_write(int inp_addr, int inp_func, int inp_sreg, int inp_nreg, unsigned char *xmt_data) -{ - int mod_error = 0; - - unsigned char tx_addr = inp_addr; - unsigned char tx_func = inp_func; - WORD2BYTE tx_sreg; tx_sreg.value = inp_sreg; - WORD2BYTE tx_nreg; tx_nreg.value = inp_nreg; - WORD2BYTE data; - unsigned char nbyt = 2*inp_nreg; - -// fill transmit buffer - - mod_clear(); - - mod_txbuf[mod_txnum++] = tx_addr; - mod_txbuf[mod_txnum++] = tx_func; - mod_txbuf[mod_txnum++] = tx_sreg.byte1; - mod_txbuf[mod_txnum++] = tx_sreg.byte0; - - if(tx_func == MOD_FUNC_SET_HREG) - { -// data.value = xmt_data[0]; -// mod_txbuf[mod_txnum++] = data.byte1; -// mod_txbuf[mod_txnum++] = data.byte0; - mod_txbuf[mod_txnum++] = xmt_data[0]; - mod_txbuf[mod_txnum++] = xmt_data[1]; - } - else - { - mod_txbuf[mod_txnum++] = tx_nreg.byte1; - mod_txbuf[mod_txnum++] = tx_nreg.byte0; - mod_txbuf[mod_txnum++] = nbyt; - for(int i=0,i2=0; i<inp_nreg; i++) - { - // data.value = xmt_data[i]; - // mod_txbuf[mod_txnum++] = data.byte1; - // mod_txbuf[mod_txnum++] = data.byte0; - mod_txbuf[mod_txnum++] = xmt_data[i2]; - mod_txbuf[mod_txnum++] = xmt_data[(i2+1)]; - i2=i2+2; - } - } - - WORD2BYTE tx_crc; tx_crc.value = mod_calc_crc(mod_txbuf, mod_txnum); - mod_txbuf[mod_txnum++] = tx_crc.byte0; - mod_txbuf[mod_txnum++] = tx_crc.byte1; - -// transmit - - mod_de = 1; - mod_re = 1; - wait_ms(1); - for(int i=0; i<mod_txnum; i++) mod.putc(mod_txbuf[i]); - wait_ms(2); - mod_de = 0; - mod_re = 0; - - wait_ms(30); - -// output transmit buffer - - if(mod_verbose) - { - printf(" mod_txbuf: %d\r\n\r\n", mod_txnum); - for(int i=0; i<mod_txnum; i++) - { - printf(" %02d", i); print_uc(1,0,1, " = ", mod_txbuf[i], "\r\n"); - } - - if(mod_txnum) - { - print_uc(1,0,1, "\r\n addr = ", tx_addr, "\r\n"); - print_uc(1,0,1, " func = ", tx_func, "\r\n"); - print_us(1,0,1, " sreg = ", tx_sreg.value, "\r\n"); - - if(tx_func == MOD_FUNC_SET_HREG) - { - data.value = xmt_data[0]; - print_us(1,0,1, " data = ", data.value, "\r\n"); - } - else - { - print_us(1,0,1, " nreg = ", tx_nreg.value, "\r\n"); - print_uc(1,0,1, " nbyt = ", nbyt, "\r\n\r\n"); - - for(int i=0; i<inp_nreg; i++) - { - printf(" data(%03d)", tx_sreg.value+i); - data.value = xmt_data[i]; - print_us(1,0,1, " = ", data.value, "\r\n"); - } - } - print_us(1,0,1, " tcrc = ", tx_crc.value, "\r\n"); - } - } - -// process received buffer - - if(mod_verbose) - { - printf("\r\n mod_rxbuf: %d\r\n\r\n", mod_rxnum); - for(int i=0; i<mod_rxnum; i++) - { - printf(" %02d", i); print_uc(1,0,1, " = ", mod_rxbuf[i], "\r\n"); - } - } - if(mod_rxnum) - { - int rxnum = 0; - unsigned char rx_addr = mod_rxbuf[rxnum++]; - unsigned char rx_func = mod_rxbuf[rxnum++]; - - if(mod_verbose) - { - print_uc(1,0,1, "\r\n addr = ", rx_addr, "\r\n"); - print_uc(1,0,1, " func = ", rx_func, ""); - } - - if(rx_func == tx_func) - { - WORD2BYTE rx_sreg; - rx_sreg.byte1 = mod_rxbuf[rxnum++]; - rx_sreg.byte0 = mod_rxbuf[rxnum++]; - - WORD2BYTE rx_nreg; - rx_nreg.byte1 = mod_rxbuf[rxnum++]; - rx_nreg.byte0 = mod_rxbuf[rxnum++]; - - WORD2BYTE rx_crc; - rx_crc.byte0 = mod_rxbuf[rxnum++]; - rx_crc.byte1 = mod_rxbuf[rxnum++]; - - if(mod_verbose) - { - print_us(1,0,1, "\r\n sreg = ", rx_sreg.value, "\r\n"); - print_us(1,0,1, " nreg = ", rx_nreg.value, "\r\n"); - print_us(1,0,1, " rcrc = ", rx_crc.value, "\r\n"); - } - } - else - { - unsigned char rx_nerr = mod_rxbuf[rxnum++]; mod_error = rx_nerr; - - if(mod_verbose) - { - if(rx_func == tx_func+0x80) printf(" (func %d error)\r\n", tx_func); - else printf(" (unknown func error)\r\n"); - - print_uc(1,0,1, " nerr = ", rx_nerr, " ("); - - switch(rx_nerr) - { - case MOD_ERROR_ILL_FUNC: ; printf("slave received illegal function code") ; break; - case MOD_ERROR_ILL_ADDR: ; printf("slave received illegal register address") ; break; - case MOD_ERROR_ILL_DATA: ; printf("slave received illegal register value") ; break; - case MOD_ERROR_SLAVE_FAIL: ; printf("slave device-specific failure") ; break; - case MOD_ERROR_SLAVE_ACK: ; printf("slave ACK") ; break; - case MOD_ERROR_SLAVE_BUSY: ; printf("slave is busy") ; break; - case MOD_ERROR_SLAVE_NACK: ; printf("slave NACK") ; break; - case MOD_ERROR_SLAVE_PARITY: ; printf("slave memory parity error") ; break; - case MOD_ERROR_RX_TIMEOUT: ; printf("master receive timeout") ; break; - case MOD_ERROR_TX_TIMEOUT: ; printf("master transmit timeout") ; break; - case MOD_ERROR_CRC: ; printf("master CRC error") ; break; - default: ; printf("unknown error") ; break; - } - printf(")\r\n"); - } - - WORD2BYTE rx_crc; - rx_crc.byte0 = mod_rxbuf[rxnum++]; - rx_crc.byte1 = mod_rxbuf[rxnum++]; - - if(mod_verbose) print_us(1,0,1, " rcrc = ", rx_crc.value, "\r\n"); - } - } - else - { - mod_error = MOD_ERROR_RX_TIMEOUT; // no received data - } - - if(mod_verbose) printf("\r\n mod_error: %d\r\n", mod_error); - - return(mod_error); -} - -// --------------------------------------------------------------------------------------------------------------------- - - -
--- a/ICE-Application/src/Drivers/mod.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,160 +0,0 @@ -#ifndef _MOD_H_ -#define _MOD_H_ - -// ------------------------------------------------------------------------------------------------- -// globals - -#define MOD_DATA 4 -#define MOD_IREG 60 -#define MOD_BAUD 19200 - -typedef struct word2byte { union -{ - unsigned short int value; - struct { unsigned char byte0; unsigned char byte1; }; -};} - WORD2BYTE; - -typedef struct sint2byte { union -{ - signed long int value; - struct { unsigned short int word0; unsigned short int word1; }; - struct { unsigned char byte0; unsigned char byte1; unsigned char byte2; unsigned char byte3; }; -};} - SINT2BYTE; - -typedef struct uint2byte { union -{ - unsigned long int value; - struct { unsigned short int word0; unsigned short int word1; }; - struct { unsigned char byte0; unsigned char byte1; unsigned char byte2; unsigned char byte3; }; -};} - UINT2BYTE; - -typedef struct float2byte { union -{ - float value; - struct { unsigned short int word0; unsigned short int word1; }; - struct { unsigned char byte0; unsigned char byte1; unsigned char byte2; unsigned char byte3; }; -};} - FLOAT2BYTE; - -typedef struct float2uint { union -{ - float value; - unsigned long int uint_value; -};} - FLOAT2UINT; - - -extern unsigned short int mod_data[MOD_DATA]; -extern unsigned short int mod_ireg[MOD_IREG]; - -extern int mod_verbose; - -// ------------------------------------------------------------------------------------------------- -// modbus standard communication structures: (from slave perspective) -// -// MOD_FUNC_GET_COIL (NBYT = total bytes of data transmitted) -// MOD_FUNC_GET_DISC -// -// byte: 0 1 2 3 4 5 6 7 -// received: ADDR FUNC SREG-H SREG-L NREG-H NREG-L RCRC-L RCRC-H -// transmit: ADDR FUNC NBYT DATA-H DATA-L TCRC-L TCRC-H NULL -// -// MOD_FUNC_GET_IREG (NBYT = total bytes of data transmitted) -// MOD_FUNC_GET_HREG -// -// byte: 0 1 2 3 4 5 6 7 -// received: ADDR FUNC SREG-H SREG-L NREG-H NREG-L RCRC-L RCRC-H -// transmit: ADDR FUNC NBYT DATA-H DATA-L TCRC-L TCRC-H NULL -// -// MOD_FUNC_SET_COIL -// -// byte: 0 1 2 3 4 5 6 7 8 -// received: ADDR FUNC SREG-H SREG-L DATA-H DATA-L RCRC-L RCRC-H -// transmit: ADDR FUNC SREG-H SREG-L DATA-H DATA-L TCRC-L TCRC-H NULL -// -// MOD_FUNC_SET_COILS -// -// byte: 0 1 2 3 4 5 6 7 8 9 10 -// received: ADDR FUNC SREG-H SREG-L NREG-H NREG-L NBYT DATA-H DATA-L RCRC-L RCRC-H -// transmit: ADDR FUNC SREG-H SREG-L NREG-H NREG-L TCRC-L TCRC-H NULL -// -// MOD_FUNC_SET_HREG -// -// byte: 0 1 2 3 4 5 6 7 8 -// received: ADDR FUNC SREG-H SREG-L DATA-H DATA-L RCRC-L RCRC-H -// transmit: ADDR FUNC SREG-H SREG-L DATA-H DATA-L TCRC-L TCRC-H NULL -// -// MOD_FUNC_SET_HREGS -// -// byte: 0 1 2 3 4 5 6 7 8 9 10 -// received: ADDR FUNC SREG-H SREG-L NREG-H NREG-L NBYT DATA-H DATA-L RCRC-L RCRC-H -// transmit: ADDR FUNC SREG-H SREG-L NREG-H NREG-L TCRC-L TCRC-H NULL -// -// response after error (EFUNC = FUNC + 0x80) -// -// byte: 0 1 2 3 4 5 -// transmit: ADDR EFUNC ERROR TCRC-L TCRC-H NULL -// -// ----------------------------------------------------------------------------- -// modbus definitions - -enum // function codes -{ - MOD_FUNC_00, - MOD_FUNC_GET_COIL, // 01: read coil status - MOD_FUNC_GET_DISC, // 02: read discrete inputs - MOD_FUNC_GET_HREG, // 03: read holding registers - MOD_FUNC_GET_IREG, // 04: read input registers - MOD_FUNC_SET_COIL, // 05: force single coil - MOD_FUNC_SET_HREG, // 06: preset single register - MOD_FUNC_07, - MOD_FUNC_08, - MOD_FUNC_09, - MOD_FUNC_10, - MOD_FUNC_11, - MOD_FUNC_12, - MOD_FUNC_13, - MOD_FUNC_14, - MOD_FUNC_SET_COILS, // 15: force multiple coils - MOD_FUNC_SET_HREGS, // 16: preset multiple registers - MOD_FUNC_NUM -}; - -enum // error codes ( * = used by Nubio ) -{ - MOD_ERROR_NONE, - MOD_ERROR_ILL_FUNC, // 01: * slave received illegal function code - MOD_ERROR_ILL_ADDR, // 02: * slave received illegal register address - MOD_ERROR_ILL_DATA, // 03: * slave received illegal register value - MOD_ERROR_SLAVE_FAIL, // 04: * slave device-specific failure - MOD_ERROR_SLAVE_ACK, // 05: * slave ACK - MOD_ERROR_SLAVE_BUSY, // 06: slave is busy - MOD_ERROR_SLAVE_NACK, // 07: * slave NACK (Nubio returns this for CRC error) - MOD_ERROR_SLAVE_PARITY, // 08: slave memory parity error - MOD_ERROR_RX_TIMEOUT, // 09: master receive timeout - MOD_ERROR_TX_TIMEOUT, // 10: master transmit timeout - MOD_ERROR_CRC, // 11: master CRC error - MOD_ERROR_NUM -}; - -// ------------------------------------------------------------------------------------------------- -// function prototypes - -void mod_init(void); -void mod_clear(void); - -void mod_process(void); - -void mod_ireg_dump(void); - -unsigned short int mod_calc_crc(unsigned char *buffer, int length); - -int mod_read(int inp_addr, int inp_func, int inp_sreg, int inp_nreg, unsigned char *rd_buf); -int mod_write(int inp_addr, int inp_func, int inp_sreg, int inp_nreg, unsigned char *xmt_buf); - -// ------------------------------------------------------------------------------------------------- - -#endif // _MOD_H_
--- a/ICE-Application/src/Drivers/rtc.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,159 +0,0 @@ -// rtc.c - real time clock (MCP7940N) -// ------------------------------------------------------------------------------------------------- - -#include "mbed.h" -#include "rtc.h" -#include "global.h" - -// ------------------------------------------------------------------------------------------------- -// globals - -int rtc_verbose = 0; - -// ------------------------------------------------------------------------------------------------- -// initialization - -void rtc_init(void) -{ - if(rtc_verbose) - { - printf("\r\n[rtc_init] \r\n\r\n"); - } - - char data[2]; - - - data[0] = RTC_CONTROL_ADDR; - - if(i2c->write(RTC_ADDR, data, RTC_SIZE_ADDR, true )) printf("no ack 1\r\n"); - if(i2c->read (RTC_ADDR, data, RTC_SIZE_CONTROL, false)) printf("no ack 2\r\n"); - - if(rtc_verbose) - { - for(int i=0; i<RTC_SIZE_CONTROL; i++) { printf(" %d = ", i); printf("%d\r\n", data[i]); }; - } - - unsigned char control_reg = data[0]; - - if((control_reg & 0x80) == 1) // !EOSC - { - data[0] = RTC_CONTROL_ADDR; - data[1] = (control_reg & 0x7F); // clear !EOSC - - if(i2c->write(RTC_ADDR, data, (RTC_SIZE_ADDR+RTC_SIZE_DATA), false)) printf("no ack 3\r\n"); - - if(rtc_verbose) printf(" !EOSC bit reset (0x%x)\r\n\r\n", data[1]); - } - - if(!rtc_verbose) printf("\r\nRTC: "); - rtc_print(); -} - -// ------------------------------------------------------------------------------------------------- -// read registers - -void rtc_regs(void) -{ - char data[RTC_SIZE_DATA]; - - if(rtc_verbose) - { - printf("\r\n[rtc_regs] \r\n\r\n"); - } - - data[0] = 0x00; // address 0x00 - - if(i2c->write(RTC_ADDR, data, RTC_SIZE_ADDR, true )) printf("no ack 1\r\n"); - if(i2c->read (RTC_ADDR, data, RTC_SIZE_DATA, false)) printf("no ack 2\r\n"); - - for(int i=0; i<RTC_SIZE_DATA; i++) { printf(" %d = ", i); printf("%d\r\n", data[i]); } -} - -// ------------------------------------------------------------------------------------------------- -// RTC functions - -void rtc_get_time(int* iyr, int* imo, int* idy, int* ihr, int* imn, int* isc) -{ - char data[RTC_SIZE_DATA]; - data[0] = 0x00; - - if(i2c->write(RTC_ADDR, data, RTC_SIZE_ADDR, true )) printf("no ack 1\r\n"); - if(i2c->read (RTC_ADDR, data, RTC_SIZE_DATA, false)) printf("no ack 2\r\n"); - - if(rtc_verbose) - { - printf("\r\n[rtc_get_time] \r\n\r\n"); - for(int i=0; i<RTC_SIZE_DATA; i++) { printf(" %d = ", i); printf("%d\r\n", data[i]); }; - } - - *isc = 10*( (data[0] & 0x70)>>4 ) + (data[0] & 0x0F); // tens = 0b01110000(0x70), ones = 0b00001111(0x0F) - *imn = 10*( (data[1] & 0x70)>>4 ) + (data[1] & 0x0F); // tens = 0b01110000(0x70), ones = 0b00001111(0x0F) - *ihr = 10*( (data[2] & 0x10)>>4 ) + (data[2] & 0x0F); // tens = 0b00110000(0x30), ones = 0b00001111(0x0F) - // day of week - *idy = 10*( (data[4] & 0x30)>>4 ) + (data[4] & 0x0F); // tens = 0b00110000(0x30), ones = 0b00001111(0x0F) - *imo = 10*( (data[5] & 0x10)>>4 ) + (data[5] & 0x0F); // tens = 0b00010000(0x10), ones = 0b00001111(0x0F) - *iyr = 10*( (data[6] & 0xF0)>>4 ) + (data[6] & 0x0F); // tens = 0b11110000(0xF0), ones = 0b00001111(0x0F) - - *iyr += 2000; -} - -void rtc_set_time(int iyr, int imo, int idy, int ihr, int imn, int isc) -{ - char data[(RTC_SIZE_DATA+RTC_SIZE_ADDR)]; - - int jyr = iyr; if(jyr >= 2000) jyr -= 2000; - - data[0] = 0x00; // address byte - data[1] = 0x00; // data byte 0: seconds - data[2] = 0x00; // data byte 1: minutes - data[3] = 0x00; // data byte 2: hours - data[4] = 0x00; // data byte 3: weekday, set to 0x00 ? (usually reads 0x21 with OSCRUN and no PWRFAIL - data[5] = 0x00; // data byte 4: day - data[6] = 0x00; // data byte 5: month - data[7] = 0x00; // data byte 6: year - - data[1] = ((isc/10)<<4) | (isc%10); // seconds - data[2] = ((imn/10)<<4) | (imn%10); // minutes - data[3] = ((ihr/10)<<4) | (ihr%10); // hours - data[5] = ((idy/10)<<4) | (idy%10); // days - data[6] = ((imo/10)<<4) | (imo%10); // months - data[7] = ((jyr/10)<<4) | (jyr%10); // years - - if(i2c->write(RTC_ADDR, data, (RTC_SIZE_DATA+RTC_SIZE_ADDR), false)) printf("no ack\r\n"); - - if(rtc_verbose) - { - printf("\r\n[rtc_set_time] \r\n\r\n"); - printf(" raw data\r\n"); - for(int i=0; i<(RTC_SIZE_DATA+RTC_SIZE_ADDR); i++) { printf(" %d = ", i); printf("%d\r\n", data[i]); }; - } -} - -void rtc_set(const char* s) -{ - int iyr, imo, idy, ihr, imn, isc; - - if(sscanf(s, "%d-%d-%d %d:%d:%d", &iyr, &imo, &idy, &ihr, &imn, &isc) != 6) - { - printf(" [rtc_set] parse error\r\n"); - } - else - { - if(rtc_verbose) printf(" date: %04d-%02d-%02d %02d:%02d:%02d\r\n", iyr, imo, idy, ihr, imn, isc); - - rtc_set_time(iyr, imo, idy, ihr, imn, isc); - - printf(" [rtc_set] "); rtc_print(); - } -} - -void rtc_print(void) -{ - int iyr=0, imo=0, idy=0, ihr=0, imn=0, isc=0; - - rtc_get_time(&iyr, &imo, &idy, &ihr, &imn, &isc); - - printf("%04d-%02d-%02d %02d:%02d:%02d", iyr, imo, idy, ihr, imn, isc); -} - -// -------------------------------------------------------------------------------------------------
--- a/ICE-Application/src/Drivers/rtc.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -#ifndef _RTC_H_ -#define _RTC_H_ - -// ------------------------------------------------------------------------------------------------- -// globals - -#define RTC_ADDR 0xD0 // address byte: 0b11010000(R/W) -> DS1339 - -#define RTC_SIZE_ADDR 1 -#define RTC_SIZE_DATA 7 -#define RTC_SIZE_CONTROL 1 -#define RTC_SIZE_STAUS 1 - -#define RTC_SECONDS_ADDR 0x0 -#define RTC_MINUTES_ADDR 0x1 -#define RTC_HOURS_ADDR 0x2 -#define RTC_DOW_ADDR 0x3 -#define RTC_MDAY_ADDR 0x4 -#define RTC_MONTH_ADDR 0x5 -#define RTC_YEAR_ADDR 0x6 - -#define RTC_CONTROL_ADDR 0xE -#define RTC_STATUS_ADDR 0xF -#define RTC_TRICKLE_ADDR 0x10 - -extern int rtc_verbose; - -// ------------------------------------------------------------------------------------------------- -// function prototypes - -void rtc_init(void); -void rtc_regs(void); - -void rtc_print(void); - -void rtc_get_time(int* iyr, int* imo, int* idy, int* ihr, int* imn, int* isc); -void rtc_set_time(int iyr, int imo, int idy, int ihr, int imn, int isc); - -void rtc_set(const char* s); - -// ------------------------------------------------------------------------------------------------- - -#endif // _RTC_H_
--- a/ICE-Application/src/ModbusMaster/ModbusMaster.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,802 +0,0 @@ -/****************************************************************************** - * - * File: ModbusMaster.cpp - * Desciption: source for the ICE Modbus Master - * - *****************************************************************************/ -#include "global.h" -#include <stdio.h> -#include "BLEDataHandler.h" -#include "ModbusMaster.h" -#include "ModbusMasterApi.h" -#include "CloudDataHandler.h" -#include "LoggerApi.h" -#include "mod.h" -#include "cJSON.h" -#include "rtc.h" -#include "rtos.h" -#include "mbed.h" -#include "v7_execute.h" -#include "utilities.h" - -DigitalOut dout1(PB_0); -DigitalOut dout2(PB_1); -DigitalIn flow_switch(PC_12); -DigitalIn dinp2(PC_11); - -/***************************************************************************** - * Function: ModbusMaster - * Description: entry point for the Modbus Master - * - * @param (IN) args (user-defined arguments) - * @return none - *****************************************************************************/ -//std::map<std::string,VirtualCommand> VirtualCommandMap; - -std::map<std::string, VirtualCommand> VirtualCommandMap; -std::map<std::string, ExecuteJavaScript> ExecuteJavaScriptMap; -std::map<std::string, HoldingRegister> HoldingRegisterMap; - -void LoadModbusConfigFile( char *fileName ); -void UpdateSimulatedInput( std::map<std::string, ModbusRegister>::iterator &modMap ); -void UpdateVirtualRegister( std::map<std::string, ModbusRegister>::iterator &modMap ); -void ReadModbusRegister( std::map<std::string, ModbusRegister>::iterator &modMap ); -void ExecuteRegisterCommand( std::string ioString, std::string Command ); -void ExecuteRegisterOperation( std::map<std::string, VirtualCommand>::iterator &cmdMap ); -void UpdateOutputRegister( std::map<std::string, ModbusRegister>::iterator &modMap ); - -char ModbusMasterScratchBuf[MAX_FILE_SIZE]; -void ModbusMaster(void const *args) -{ - printf("%s ModbusMaster has started...", __func__); - bool SignaledMain = false; - std::map<std::string, ModbusRegister>::iterator modMap; - - mod_init(); - DigitalOut mod_power(PA_8); - mod_power = 0; // provide power to the modbus - -#ifdef EXECUTE_SCRIPT - v7_Create_Engine(); -#endif - -#ifdef LOAD_PERSISTENT_CONFIGURATIONS - std::vector<std::string>::iterator file; - std::vector<std::string> file_list; - file_list = GLOBAL_mdot->listUserFiles(); - for(file = file_list.begin(); file != file_list.end(); ++file) { -// printf("%s:%d: filename:%s\r\n", __func__, __LINE__, file->c_str() ); - LoadModbusConfigFile( (char *)file->c_str() ); - } -#endif - - while ( true ) { - for (modMap = ModbusRegisterMap.begin(); modMap != ModbusRegisterMap.end(); ++modMap) { - if( modMap->second.simulated == true ) { - UpdateSimulatedInput( modMap ); - continue; - } else if( modMap->second.node != 0 ) { - ReadModbusRegister( modMap ); - } else if( (modMap->second.node == 0) && (modMap->second.regType == REG_TYPE_INPUT) ) { -// printf("processing PIN input=%s, reg=%d, value=%d",modMap->first.c_str(), ModbusRegisterMap[modMap->first].reg, (bool)RegisterValueMap[modMap->first].float_value); - if( ModbusRegisterMap[modMap->first].reg == 1 ) { - // digital input - RegisterValueMap[modMap->first].float_value = (float)flow_switch.read(); - } else { - RegisterValueMap[modMap->first].float_value = (float)dinp2.read(); - } - } else if( modMap->second.regType == REG_TYPE_VINPUT ) { - UpdateVirtualRegister( modMap ); - } - } - - // now that all of the modbus registers are updated we can execute the register commands. -#ifdef EXECUTE_SCRIPT - std::map<std::string, ExecuteJavaScript>::iterator jsMap; - for (jsMap = ExecuteJavaScriptMap.begin(); jsMap != ExecuteJavaScriptMap.end(); ++jsMap) { -// printf("%s:%d: %s\r\n", __func__, __LINE__, Util_getHeapData().c_str()); - v7_Execute_Script( jsMap->second.script, jsMap->second.argv ); - } -#else - for (modMap = ModbusRegisterMap.begin(); modMap != ModbusRegisterMap.end(); ++modMap) { - ExecuteRegisterCommand( modMap->first, modMap->second.cmd ); - } -#endif - - // now that all of the inputs and virtuals have been updated, go through the outputs. - for (modMap = ModbusRegisterMap.begin(); modMap != ModbusRegisterMap.end(); ++modMap) { - if( modMap->second.regType == REG_TYPE_OUTPUT ) { - UpdateOutputRegister( modMap ); - } - } - - osEvent evt = ModbusMasterMailBox.get(50); - if (evt.status == osEventMail) { - ModbusMasterReq_t *mail = (ModbusMasterReq_t*)evt.value.p; - if( mail->action == ACTION_EXEC_CMD ) { - printf("Mail Received: Action: %d, Executing Command: %s\r\n", mail->action, mail->msg); - ModbusMasterExecCmd( mail->replyThread, mail->msg ); - } else { - printf("Mail Received: Action: %d, New Input File: %s\r\n", mail->action, mail->msg); - LoadModbusConfigFile( mail->msg ); - } - ModbusMasterMailBox.free(mail); - } - if( SignaledMain == false ) { - SignaledMain = true; - osSignalSet(mainThreadId, sig_output_continue); - } - Thread::wait(5000); - } -} - -bool ReadModbus_32bit_float( float *float_value, int order, unsigned char *rd_buf ) -{ - MR_REGISTER_32_BIT_FLOAT value; - - switch( order ) { - case BigEndian: - value.b.lo_lo = rd_buf[3]; - value.b.lo_hi = rd_buf[2]; - value.b.hi_lo = rd_buf[1]; - value.b.hi_hi = rd_buf[0]; - break; - case BigEndianReverseWord: - value.b.lo_lo = rd_buf[1]; - value.b.lo_hi = rd_buf[0]; - value.b.hi_lo = rd_buf[3]; - value.b.hi_hi = rd_buf[2]; - break; - default: - printf("%s:%d: order not supported\r\n",__func__,__LINE__); - return false; - } -// printf("0x%x 0x%x 0x%x 0x%x (%2.4f)\r\n", value.b.hi_hi, value.b.hi_lo, value.b.lo_hi, value.b.lo_lo, value.f); - *float_value = value.f; - return true; -} - -bool WriteModbus_32bit_float( float float_value, int order, unsigned char *xmt_buf ) -{ - MR_REGISTER_32_BIT_FLOAT value; - - value.f = float_value; - - switch( order ) { - case BigEndian: - xmt_buf[3] = value.b.lo_lo; - xmt_buf[2] = value.b.lo_hi; - xmt_buf[1] = value.b.hi_lo; - xmt_buf[0] = value.b.hi_hi; - break; - case BigEndianReverseWord: - xmt_buf[1] = value.b.lo_lo; - xmt_buf[0] = value.b.lo_hi; - xmt_buf[3] = value.b.hi_lo; - xmt_buf[2] = value.b.hi_hi; - break; - default: - printf("%s:%d: order not supported\r\n",__func__,__LINE__); - return false; - } - printf("%s:%d: 0x%x 0x%x 0x%x 0x%x (%2.4f)\r\n",__func__,__LINE__, value.b.hi_hi, value.b.hi_lo, value.b.lo_hi, value.b.lo_lo, value.f); - return true; -} - -bool WriteModbus_Multiple( std::vector<uint16_t> XmtData, int order, unsigned char *xmt_buf ) -{ - MR_REGISTER_32_BIT_FLOAT value; - -// std::vector<uint16_t>::iterator iter; -// for (iter = XmtData.begin(); iter != XmtData.end(); ++iter) { -// log_event << "{\"t\":"<< "\"" << iter->c_str() << "\"," << "\"v\":"<< "\"" << ModbusRegisterMap[*iter].float_value<< "\"},"; -// } - - switch( order ) { - case BigEndian: - xmt_buf[3] = value.b.lo_lo; - xmt_buf[2] = value.b.lo_hi; - xmt_buf[1] = value.b.hi_lo; - xmt_buf[0] = value.b.hi_hi; - break; - case BigEndianReverseWord: - xmt_buf[1] = value.b.lo_lo; - xmt_buf[0] = value.b.lo_hi; - xmt_buf[3] = value.b.hi_lo; - xmt_buf[2] = value.b.hi_hi; - break; - default: - printf("%s:%d: order not supported\r\n",__func__,__LINE__); - return false; - } - printf("%s:%d: 0x%x 0x%x 0x%x 0x%x (%2.4f)\r\n",__func__,__LINE__, value.b.hi_hi, value.b.hi_lo, value.b.lo_hi, value.b.lo_lo, value.f); - return true; -} - -bool ReadModbus_32bit_int( int32_t *int32_value, int order, unsigned char *rd_buf ) -{ - MR_REGISTER_32BIT_INT value; - - switch( order ) { - case BigEndian: - value.b.lo_lo = rd_buf[3]; - value.b.lo_hi = rd_buf[2]; - value.b.hi_lo = rd_buf[1]; - value.b.hi_hi = rd_buf[0]; - break; - case BigEndianReverseWord: - value.b.lo_lo = rd_buf[1]; - value.b.lo_hi = rd_buf[0]; - value.b.hi_lo = rd_buf[3]; - value.b.hi_hi = rd_buf[2]; - break; - default: - printf("%s:%d: order not supported\r\n",__func__,__LINE__); - return false; - } -// printf("0x%x 0x%x 0x%x 0x%x (%d)\r\n", value.b.hi_hi, value.b.hi_lo, value.b.lo_hi, value.b.lo_lo, value.i); - *int32_value = value.i; - return true; -} - -bool WriteModbus_32bit_int( int32_t int32_value, int order, unsigned char *xmt_buf ) -{ - MR_REGISTER_32BIT_INT value; - - value.i = int32_value; - - switch( order ) { - case BigEndian: - xmt_buf[3] = value.b.lo_lo; - xmt_buf[2] = value.b.lo_hi; - xmt_buf[1] = value.b.hi_lo; - xmt_buf[0] = value.b.hi_hi; - break; - case BigEndianReverseWord: - xmt_buf[1] = value.b.lo_lo; - xmt_buf[0] = value.b.lo_hi; - xmt_buf[3] = value.b.hi_lo; - xmt_buf[2] = value.b.hi_hi; - break; - default: - printf("%s:%d: order not supported\r\n",__func__,__LINE__); - return false; - } - printf("%s:%d: 0x%x 0x%x 0x%x 0x%x (%d)\r\n", __func__, __LINE__, value.b.hi_hi, value.b.hi_lo, value.b.lo_hi, value.b.lo_lo, value.i); - return true; -} - -bool WriteModbus_16bit_int( int16_t int16_value, int order, unsigned char *xmt_buf ) -{ - MR_REGISTER_16BIT_INT value; - - value.w = int16_value; - - switch( order ) { - case BigEndian: - case BigEndianReverseWord: - xmt_buf[1] = value.b.lo; - xmt_buf[0] = value.b.hi; - break; - default: - printf("%s:%d: order not supported\r\n",__func__,__LINE__); - return false; - } - printf("%s:%d: 0x%x 0x%x (%d)\r\n", __func__,__LINE__, value.b.hi, value.b.lo, value.w); - return true; -} - -void ModbusMasterExecCmd( ThreadName_t replyTo, char *cmd ) -{ - bool status; - int ret; - int node, func, sreg, nreg, dtype, order; - float value; - unsigned char rd_buf[16]; - std::vector<uint16_t> XmtData; - - printf("%s:%d: command=%s\r\n", __func__, __LINE__, cmd ); - - cJSON * root = cJSON_Parse(cmd); - std::string id = cJSON_GetObjectItem(root,"id")->valuestring; - if ( cJSON_HasObjectItem(root, "holding") ) { - - std::string key = cJSON_GetObjectItem(root,"holding")->valuestring; - node = HoldingRegisterMap[key].node; - sreg = HoldingRegisterMap[key].sreg; - nreg = HoldingRegisterMap[key].nreg; - order = HoldingRegisterMap[key].order; - func = MODBUS_WRITE_MULTIPLE_HOLDING; - dtype = TYPE_MULTI_BYTE; - printf("%s:%d: HOLDING REGISTER COMMAND, tag=%s, node=%d, sreg=%d, nreg=%d, order=%d, func=%d\r\n", __func__,__LINE__, key.c_str(), node, sreg, nreg, order, func); - cJSON *data = cJSON_GetObjectItem(root, "data"); - for ( int i = 0; i < cJSON_GetArraySize(data); ++i ) { - cJSON *item = cJSON_GetArrayItem(data, i); - uint16_t item_value = atoi(cJSON_GetObjectItem(item, "v")->valuestring); - XmtData.push_back(item_value); - printf("Pushing data: %d\r\n",item_value); - } - } else { - node = atoi(cJSON_GetObjectItem(root,"node")->valuestring); - func = atoi(cJSON_GetObjectItem(root,"func")->valuestring); - sreg = atoi(cJSON_GetObjectItem(root,"sreg")->valuestring); - nreg = atoi(cJSON_GetObjectItem(root,"nreg")->valuestring); - dtype = atoi(cJSON_GetObjectItem(root,"dtype")->valuestring); - order = atoi(cJSON_GetObjectItem(root,"order")->valuestring); - value = atof(cJSON_GetObjectItem(root,"value")->valuestring); - if( dtype == TYPE_MULTI_BYTE ) { - if ( cJSON_HasObjectItem(root, "data") ) { - cJSON *data = cJSON_GetObjectItem(root, "data"); - for ( int i = 0; i < cJSON_GetArraySize(data); ++i ) { - cJSON *item = cJSON_GetArrayItem(data, i); - uint16_t item_value = atoi(cJSON_GetObjectItem(item, "v")->valuestring); - XmtData.push_back(item_value); - printf("Pushing data: %d\r\n",item_value); - } - } else { - printf("NO DATA FOR WRITE MULTIPLE HOLDING REGISTERS\r\n"); - ReplyToHandler( replyTo, id, DATA_ARRAY_MISSING, 0 ); - return; - } - } - } - cJSON_Delete(root); - - switch( func ) { - case MOD_FUNC_GET_HREG: // read holding register - case MOD_FUNC_GET_IREG: // read input register - ret = mod_read(node, func, sreg, nreg, rd_buf); - if( ret != MOD_ERROR_NONE ) { - ReplyToHandler( replyTo, id, ret, 0 ); - printf("CMD: %s:%d: %s failed, errflag=%d\r\n", __func__, __LINE__, id.c_str(), ret ); - break; - } - switch( dtype ) { - case TYPE_32BIT_FLOAT: { - float float_value; - status = ReadModbus_32bit_float( &float_value, order, rd_buf ); - if( status == true ) { - printf("CMD: %s:%d: %s value=%2.4f\r\n", __func__, __LINE__, id.c_str(), float_value ); - ret = SUCCESS; - } else { - printf("CMD: %s:%d: %s failed\r\n", __func__, __LINE__, id.c_str() ); - ret = ORDER_NOT_SUPPORTED; - } - ReplyToHandler( replyTo, id, ret, float_value ); - break; - } - case TYPE_32BIT_INT: - case TYPE_32BIT_UINT: { - int32_t int32_value; - status = ReadModbus_32bit_int( &int32_value, order, rd_buf ); - if( status == true ) { - printf("CMD: %s:%d: %s value=%d\r\n", __func__, __LINE__, id.c_str(), int32_value ); - ret = SUCCESS; - } else { - printf("CMD: %s:%d: %s failed\r\n", __func__, __LINE__, id.c_str() ); - ret = ORDER_NOT_SUPPORTED; - } - ReplyToHandler( replyTo, id, ret, (float)int32_value ); - break; - } - case TYPE_16BIT_INT: - case TYPE_16BIT_UINT: - break; - default: - break; - } - break; - case MOD_FUNC_SET_HREG: // write holding register - case MOD_FUNC_SET_HREGS: // write multiple registers (only supports 2 right now) - case MOD_FUNC_SET_COIL: { // write coil - unsigned char xmt_buf[10]; - switch( dtype ) { - case TYPE_32BIT_FLOAT: { - status = WriteModbus_32bit_float( value, order, xmt_buf ); - if( status != true ) { - printf("CMD: %s:%d: %s failed\r\n", __func__, __LINE__, id.c_str() ); - return; - } - printf("%s:%d: 0x%x 0x%x 0x%x 0x%x\r\n", __func__,__LINE__, xmt_buf[0], xmt_buf[1], xmt_buf[2], xmt_buf[3]); - break; - } - case TYPE_32BIT_INT: - case TYPE_32BIT_UINT: { - status = WriteModbus_32bit_int( (int32_t)value, order, xmt_buf ); - if( status != true ) { - printf("CMD: %s:%d: %s failed\r\n", __func__, __LINE__, id.c_str() ); - return; - } - break; - } - case TYPE_16BIT_INT: - case TYPE_16BIT_UINT: - status = WriteModbus_16bit_int( (int16_t)value, order, xmt_buf ); - if( status != true ) { - printf("CMD: %s:%d: %s failed\r\n", __func__, __LINE__, id.c_str() ); - return; - } - printf("%s:%d: 0x%x 0x%x\r\n", __func__,__LINE__, xmt_buf[0], xmt_buf[1]); - break; - case TYPE_MULTI_BYTE : - printf("CMD: %s:%d: WRITE MULTI-BYTE NOT IMPLEMENTED\r\n", __func__, __LINE__ ); - ReplyToHandler( replyTo, id, UNKNOWN_OPERATION, 0 ); - return; - //break; - default: - printf("CMD: %s:%d: %s NOT IMPLEMENTED\r\n", __func__, __LINE__, id.c_str() ); - return; - } - printf("%s:%d: 0x%x 0x%x 0x%x 0x%x\r\n", __func__,__LINE__, xmt_buf[0], xmt_buf[1], xmt_buf[2], xmt_buf[3]); - ret = mod_write(node, func, sreg, nreg, xmt_buf); - if( ret != MOD_ERROR_NONE ) { - printf("CMD: %s:%d: %s failed, errflag=%d\r\n", __func__, __LINE__, id.c_str(), ret ); - } else { - printf("CMD: %s:%d: %s wrote to modbus func=%d reg=%d value=%2.4f, errflag=%d\r\n", __func__, __LINE__, id.c_str(), func, sreg, value, ret ); - } - ReplyToHandler( replyTo, id, ret, 0 ); - break; - } - default: - printf("CMD: %s:%d: %s failed, errflag=%d\r\n", __func__, __LINE__, id.c_str(), ret ); - ReplyToHandler( replyTo, id, UNKNOWN_OPERATION, 0 ); - break; - } -} - -void LoadModbusConfigFile( char *fileName ) -{ - bool status; - RegisterType_t regType; - -// printf("%s:%d: Loading Config file: %s\r\n", __func__, __LINE__, fileName); - - if( (strncmp( fileName, "cmd_", (strlen("cmd_")-1)) == 0) ) { - status = GLOBAL_mdot->readUserFile(fileName, ModbusMasterScratchBuf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)read file failed, status=%d\r\n", __LINE__, status); - return; - } - - cJSON * root = cJSON_Parse(ModbusMasterScratchBuf); - std::string id = cJSON_GetObjectItem(root,"id")->valuestring; - VirtualCommandMap[id].Constant = atof(cJSON_GetObjectItem(root,"Constant")->valuestring); - VirtualCommandMap[id].Operator = cJSON_GetObjectItem(root,"Operator")->valuestring; - VirtualCommandMap[id].Operand = cJSON_GetObjectItem(root,"Operand")->valuestring; - printf("Loaded command file: id=%s, Operand=%s, Operator=%s, Constant=%.4f\r\n", id.c_str(), VirtualCommandMap[id].Operand.c_str(), VirtualCommandMap[id].Operator.c_str(), VirtualCommandMap[id].Constant); - cJSON_Delete(root); - return; - } - - if( (strncmp( fileName, "vreg_", (strlen("vreg_")-1)) == 0) ) { - printf("Loading Virtual Register File: %s\r\n", fileName); - status = GLOBAL_mdot->readUserFile(fileName, ModbusMasterScratchBuf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)read file failed, status=%d\r\n", __LINE__, status); - return; - } - - cJSON * root = cJSON_Parse(ModbusMasterScratchBuf); - std::string id = cJSON_GetObjectItem(root,"id")->valuestring; - RegisterValueMap[id].float_value = atof(cJSON_GetObjectItem(root,"value")->valuestring); - RegisterValueMap[id].errflag = 0; - printf("Loaded Virtual Register file: id=%s, value=%.4f\r\n", id.c_str(), RegisterValueMap[id].float_value); - cJSON_Delete(root); - return; - } - -#ifdef EXECUTE_SCRIPT - if( (strncmp( fileName, "js_", (strlen("js_")-1)) == 0) ) { - printf("Loading JavaScript file: %s\r\n", fileName); - status = GLOBAL_mdot->readUserFile(fileName, ModbusMasterScratchBuf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)read file failed, status=%d\r\n", __LINE__, status); - return; - } - v7_Load_Script( (const char *)ModbusMasterScratchBuf ); -// printf("Loaded function: %s\r\n", ModbusMasterScratchBuf); - return; - } - - if( (strncmp( fileName, "exe_js_", (strlen("exe_js_")-1)) == 0) ) { - printf("Loading JavaScript Execution file: %s\r\n", fileName); - status = GLOBAL_mdot->readUserFile(fileName, ModbusMasterScratchBuf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)read file failed, status=%d\r\n", __LINE__, status); - return; - } - cJSON * root = cJSON_Parse(ModbusMasterScratchBuf); - std::string id = cJSON_GetObjectItem(root,"id")->valuestring; - ExecuteJavaScriptMap[id].script = cJSON_GetObjectItem(root, "script")->valuestring; - cJSON *args = cJSON_GetObjectItem(root, "args"); - printf("%s:%d: Number of array items: %d\r\n", __func__, __LINE__, cJSON_GetArraySize(args) ); - for ( int i = 0; i < cJSON_GetArraySize(args); ++i ) { - cJSON *item = cJSON_GetArrayItem(args, i); - ExecuteJavaScriptMap[id].argv[i] = cJSON_GetObjectItem(item, "arg")->valuestring; -// printf("Pushing tag data %s\r\n", cJSON_GetObjectItem(item, "arg")->valuestring); - } - printf("Loaded JavaScript Execute File: id=%s, script=%s, argv0=%s, argv0=%s, argv0=%s, argv0=%s\r\n", id.c_str(), ExecuteJavaScriptMap[id].script.c_str(), ExecuteJavaScriptMap[id].argv[0].c_str(),ExecuteJavaScriptMap[id].argv[1].c_str(),ExecuteJavaScriptMap[id].argv[2].c_str(),ExecuteJavaScriptMap[id].argv[3].c_str() ); - cJSON_Delete(root); - return; - } -#endif - - if( (strncmp( fileName, "hold", (strlen("hold")-1)) == 0) ) { - status = GLOBAL_mdot->readUserFile(fileName, ModbusMasterScratchBuf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)read file failed, status=%d\r\n", __LINE__, status); - return; - } - - cJSON * root = cJSON_Parse(ModbusMasterScratchBuf); - std::string id = cJSON_GetObjectItem(root,"id")->valuestring; - HoldingRegisterMap[id].node = atoi(cJSON_GetObjectItem(root,"node")->valuestring); - HoldingRegisterMap[id].sreg = atoi(cJSON_GetObjectItem(root,"sreg")->valuestring); - HoldingRegisterMap[id].nreg = atoi(cJSON_GetObjectItem(root,"nreg")->valuestring); - HoldingRegisterMap[id].order = atoi(cJSON_GetObjectItem(root,"order")->valuestring); - printf("Loaded holding register: id=%s, node=%d, sreg=%d, nreg=%d, order=%d\r\n", id.c_str(), HoldingRegisterMap[id].node, HoldingRegisterMap[id].sreg, HoldingRegisterMap[id].nreg, HoldingRegisterMap[id].order); - cJSON_Delete(root); - return; - } - - regType = REG_TYPE_NONE; - - if( (strncmp( fileName, "input", (strlen("input")-1)) == 0) ) { - regType = REG_TYPE_INPUT; - } else if( (strncmp( fileName, "output", (strlen("output")-1)) == 0) ) { - regType = REG_TYPE_OUTPUT; - } else if( (strncmp( fileName, "vinput", (strlen("vinput")-1)) == 0) ) { - regType = REG_TYPE_VINPUT; - } else if( (strncmp( fileName, "voutput", (strlen("voutput")-1)) == 0) ) { - regType = REG_TYPE_VOUTPUT; - } - - if( regType != REG_TYPE_NONE ) { - status = GLOBAL_mdot->readUserFile(fileName, ModbusMasterScratchBuf, MAX_FILE_SIZE); - if( status != true ) { - printf("(%d)read file failed, status=%d", __LINE__, status); - return; - } else { -// printf("(%s:%d)loading File: %s\r\n", __func__, __LINE__, fileName ); -// printf("%s:%d:Loaded:%s\r\n", __func__, __LINE__, ModbusMasterScratchBuf ); - } - - cJSON * root = cJSON_Parse(ModbusMasterScratchBuf); - if( !cJSON_HasObjectItem(root,"id") ) - { - printf("%s:%d: INVALID JSON STRING\r\n", __func__,__LINE__); - return; - } - std::string id = cJSON_GetObjectItem(root,"id")->valuestring; - ModbusRegisterMap[id].min = atof(cJSON_GetObjectItem(root,"min")->valuestring); - ModbusRegisterMap[id].max = atof(cJSON_GetObjectItem(root,"max")->valuestring); - ModbusRegisterMap[id].node = atoi(cJSON_GetObjectItem(root,"node")->valuestring); - ModbusRegisterMap[id].reg = atoi(cJSON_GetObjectItem(root,"reg")->valuestring); - ModbusRegisterMap[id].rtype = atoi(cJSON_GetObjectItem(root,"rtype")->valuestring); - ModbusRegisterMap[id].type = atoi(cJSON_GetObjectItem(root,"type")->valuestring); - ModbusRegisterMap[id].size = atoi(cJSON_GetObjectItem(root,"size")->valuestring); - ModbusRegisterMap[id].order = atoi(cJSON_GetObjectItem(root,"order")->valuestring); - ModbusRegisterMap[id].rfreq = atoi(cJSON_GetObjectItem(root,"rfreq")->valuestring); - ModbusRegisterMap[id].regType = regType; - ModbusRegisterMap[id].simulated = false; - RegisterValueMap[id].errflag = 0; - ModbusRegisterMap[id].cmd = cJSON_GetObjectItem(root,"cmd")->valuestring; - cJSON_Delete(root);; - } - return; -} - -void UpdateSimulatedInput( std::map<std::string, ModbusRegister>::iterator &modMap ) -{ - if ( SimulateInputMap[modMap->first].errflag ) { - RegisterValueMap[modMap->first].errflag = SimulateInputMap[modMap->first].errflag; - } else { - RegisterValueMap[modMap->first].errflag = 0; - } - // printf("\r\nsimulating input=%s, min=%2.4f, max=%2.4f, start_value=%2.4f, up_step=%2.4f, down_step=%2.4f moving_up=%d\r\n",modMap->first.c_str(), SimulateInputMap[modMap->first].min, SimulateInputMap[modMap->first].max, SimulateInputMap[modMap->first].start_value, SimulateInputMap[modMap->first].up_step, SimulateInputMap[modMap->first].down_step, SimulateInputMap[modMap->first].moving_up); - if( (SimulateInputMap[modMap->first].min == 0) && (SimulateInputMap[modMap->first].max == 0) ) { - RegisterValueMap[modMap->first].float_value = SimulateInputMap[modMap->first].start_value; -// printf("simulating input=%s, value=%2.4f\r\n",modMap->first.c_str(), RegisterValueMap[modMap->first].float_value); - } else { - if( RegisterValueMap[modMap->first].float_value >= SimulateInputMap[modMap->first].max ) { - SimulateInputMap[modMap->first].moving_up = false; -// printf("simulating down input=%s, value=%2.4f - %2.4f\r\n",modMap->first.c_str(), RegisterValueMap[modMap->first].float_value, SimulateInputMap[modMap->first].down_step); - RegisterValueMap[modMap->first].float_value = RegisterValueMap[modMap->first].float_value - SimulateInputMap[modMap->first].down_step; - } else if( RegisterValueMap[modMap->first].float_value <= SimulateInputMap[modMap->first].min ) { - SimulateInputMap[modMap->first].moving_up = true; -// printf("simulating up input=%s, value=%2.4f + %2.4f\r\n",modMap->first.c_str(), RegisterValueMap[modMap->first].float_value, SimulateInputMap[modMap->first].up_step); - RegisterValueMap[modMap->first].float_value = RegisterValueMap[modMap->first].float_value + SimulateInputMap[modMap->first].up_step; - } else { - if( SimulateInputMap[modMap->first].moving_up == true ) { -// printf("continue simulate up input=%s, value=%2.4f + %2.4f\r\n",modMap->first.c_str(), RegisterValueMap[modMap->first].float_value, SimulateInputMap[modMap->first].up_step); - RegisterValueMap[modMap->first].float_value = RegisterValueMap[modMap->first].float_value + SimulateInputMap[modMap->first].up_step; - } else { -// printf("continue simulate down input=%s, value=%2.4f - %2.4f\r\n",modMap->first.c_str(), RegisterValueMap[modMap->first].float_value, SimulateInputMap[modMap->first].down_step); - RegisterValueMap[modMap->first].float_value = RegisterValueMap[modMap->first].float_value - SimulateInputMap[modMap->first].down_step; - } - } -// printf("simulating input=%s, value=%2.4f\r\n\r\n",modMap->first.c_str(), RegisterValueMap[modMap->first].float_value); - } -} - -void UpdateVirtualRegister( std::map<std::string, ModbusRegister>::iterator &modMap ) -{ -} - -void ReadModbusRegister( std::map<std::string, ModbusRegister>::iterator &modMap ) -{ - bool status; - unsigned char rd_buf[16]; - memset( rd_buf, 0, 16 ); -// printf("Processing Input: tag=%s, node=%d, reg=%d, size=%d, order=%d", modMap->first.c_str(), modMap->second.node, modMap->second.reg, modMap->second.size, modMap->second.order ); - int ret = mod_read(modMap->second.node, modMap->second.rtype, modMap->second.reg, modMap->second.size, rd_buf); - switch( modMap->second.type ) { - case TYPE_32BIT_FLOAT: - float float_value; - if( ret != MOD_ERROR_NONE ) { - RegisterValueMap[modMap->first].errflag = ret; - break; - } - status = ReadModbus_32bit_float( &float_value, modMap->second.order, rd_buf ); - if( status == true ) { - RegisterValueMap[modMap->first].float_value = float_value; - RegisterValueMap[modMap->first].errflag = 0; -// printf("Modbus Tag:%s value=%2.4f", modMap->first.c_str(), float_value ); - } else { - RegisterValueMap[modMap->first].errflag = 1000; -// printf("Modbus Read Failed, tag=%s", modMap->first.c_str() ); - } - break; - case TYPE_32BIT_INT: - int32_t int32_value; - if( ret != MOD_ERROR_NONE ) { - RegisterValueMap[modMap->first].errflag = ret; - break; - } - status = ReadModbus_32bit_int( &int32_value, modMap->second.order, rd_buf ); - if( status == true ) { - RegisterValueMap[modMap->first].float_value = int32_value; - RegisterValueMap[modMap->first].errflag = 0; - printf("Modbus Tag:%s value=%2.4f", modMap->first.c_str(), RegisterValueMap[modMap->first].float_value ); - } else { - RegisterValueMap[modMap->first].errflag = 1000; - printf("Modbus Read Failed, tag=%s", modMap->first.c_str() ); - } - break; - case TYPE_32BIT_UINT: - break; - case TYPE_16BIT_INT: - break; - case TYPE_16BIT_UINT: - break; - default: - break; - } -} - -void ExecuteRegisterCommand( std::string ioTag, std::string Command ) -{ - if( Command.size() == 0 ) { - return; - } -// printf("Executing Register Commnand: %s for %s\r\n",Command.c_str(), ioTag.c_str()); - float operandValue = 0; - float ioTagValue = RegisterValueMap[ioTag].float_value; - if( VirtualCommandMap[Command].Operand.size() != 0 ) { - operandValue = RegisterValueMap[VirtualCommandMap[Command].Operand].float_value; - } - float constantValue = VirtualCommandMap[Command].Constant; - - switch( VirtualCommandMap[Command].Operator.c_str()[0] ) { - case '=': - if( VirtualCommandMap[Command].Operand.size() == 0 ) { - RegisterValueMap[ioTag].float_value = constantValue; - } else { - RegisterValueMap[ioTag].float_value = operandValue; - } -// printf("Setting tag=%s, equal to (value=%2.4f)\r\n", ioTag.c_str(), ModbusRegisterMap[ioTag].float_value ); - break; - case '*': { - if( VirtualCommandMap[Command].Operand.size() == 0 ) { - RegisterValueMap[ioTag].float_value = ioTagValue * constantValue; -// printf("Setting tag=%s, equal to (%2.4f*%2.4f) = %2.4f\r\n", ioTag.c_str(), ioTagValue, constantValue, ModbusRegisterMap[ioTag].float_value ); - } else { - RegisterValueMap[ioTag].float_value = ioTagValue * operandValue; -// printf("Setting tag=%s, equal to (%2.4f*%2.4f) = %2.4f\r\n", ioTag.c_str(), ioTagValue, operandValue, ModbusRegisterMap[ioTag].float_value ); - } - break; - } - case '/': { - if( VirtualCommandMap[Command].Operand.size() == 0 ) { - // constant operation - RegisterValueMap[ioTag].float_value = ioTagValue / constantValue; -// printf("Setting tag=%s, equal to (%2.4f/%2.4f) = %2.4f\r\n", ioTag.c_str(), ioTagValue, constantValue, ModbusRegisterMap[ioTag].float_value ); - } else if( operandValue != 0 ) { - RegisterValueMap[ioTag].float_value = ioTagValue / operandValue; -// printf("Setting tag=%s, equal to (%2.4f/%2.4f) = %2.4f\r\n", ioTag.c_str(), ioTagValue, operandValue, ModbusRegisterMap[ioTag].float_value ); - } else { -// printf("NOT DOING DIVIDE BY ZERO\r\n"); - } - break; - } - case '+': { - if( VirtualCommandMap[Command].Operand.size() == 0 ) { - RegisterValueMap[ioTag].float_value = ioTagValue + constantValue; -// printf("Setting tag=%s, equal to (%2.4f+%2.4f) = %2.4f\r\n", ioTag.c_str(), ioTagValue, constantValue, ModbusRegisterMap[ioTag].float_value ); - } else { - RegisterValueMap[ioTag].float_value = ioTagValue + operandValue; -// printf("Setting tag=%s, equal to (%2.4f+%2.4f) = %2.4f\r\n", ioTag.c_str(), ioTagValue, operandValue, ModbusRegisterMap[ioTag].float_value ); - } - break; - } - case '-': { - if( VirtualCommandMap[Command].Operand.size() == 0 ) { - RegisterValueMap[ioTag].float_value = ioTagValue - constantValue; -// printf("Setting tag=%s, equal to (%2.4f-%2.4f) = %2.4f\r\n", ioTag.c_str(), ioTagValue, constantValue, ModbusRegisterMap[ioTag].float_value ); - } else { - RegisterValueMap[ioTag].float_value = ioTagValue - operandValue; -// printf("Setting tag=%s, equal to (%2.4f-%2.4f) = %2.4f\r\n", ioTag.c_str(), ioTagValue, operandValue, ModbusRegisterMap[ioTag].float_value ); - } - break; - } - default: - printf("OPERATION NOT SUPPORTED: %s\r\n", VirtualCommandMap[Command].Operator.c_str()); - break; - } -} - -void UpdateOutputRegister( std::map<std::string, ModbusRegister>::iterator &modMap ) -{ - bool status; - int ret; - - if( modMap->second.node == 0) { - if( ModbusRegisterMap[modMap->first].reg == 1 ) { - dout1 = (bool)((int)RegisterValueMap[modMap->first].float_value&0x1); - } else { - dout2 = (bool)((int)RegisterValueMap[modMap->first].float_value&0x1); - } - } else { - unsigned char xmt_buf[10]; - - switch( ModbusRegisterMap[modMap->first].type ) { - case TYPE_32BIT_FLOAT: { - status = WriteModbus_32bit_float( RegisterValueMap[modMap->first].float_value, ModbusRegisterMap[modMap->first].order, xmt_buf ); - if( status != true ) { - printf("CMD: %s:%d: failed\r\n", __func__, __LINE__); - return; - } - printf("%s:%d: 0x%x 0x%x 0x%x 0x%x\r\n", __func__,__LINE__, xmt_buf[0], xmt_buf[1], xmt_buf[2], xmt_buf[3]); - break; - } - case TYPE_32BIT_INT: - case TYPE_32BIT_UINT: { - status = WriteModbus_32bit_int( ((int32_t)RegisterValueMap[modMap->first].float_value&0x1), ModbusRegisterMap[modMap->first].order, xmt_buf ); - if( status != true ) { - printf("CMD: %s:%d: failed\r\n", __func__, __LINE__ ); - return; - } - break; - } - case TYPE_16BIT_INT: - case TYPE_16BIT_UINT: - status = WriteModbus_16bit_int( ((int16_t)RegisterValueMap[modMap->first].float_value&0x1), ModbusRegisterMap[modMap->first].order, xmt_buf ); - if( status != true ) { - printf("CMD: %s:%d: failed\r\n", __func__, __LINE__ ); - return; - } - printf("%s:%d: 0x%x 0x%x\r\n", __func__,__LINE__, xmt_buf[0], xmt_buf[1]); - break; - default: - printf("CMD: %s:%d: NOT IMPLEMENTED\r\n", __func__, __LINE__ ); - return; - } - printf("%s:%d: 0x%x 0x%x 0x%x 0x%x\r\n", __func__,__LINE__, xmt_buf[0], xmt_buf[1], xmt_buf[2], xmt_buf[3]); - ret = mod_write(ModbusRegisterMap[modMap->first].node, ModbusRegisterMap[modMap->first].rtype, ModbusRegisterMap[modMap->first].reg, ModbusRegisterMap[modMap->first].size, xmt_buf); - if( ret != MOD_ERROR_NONE ) { - printf("CMD: %s:%d: failed, errflag=%d\r\n", __func__, __LINE__, ret ); - } else { - printf("CMD: %s:%d: wrote to modbus func=%d reg=%d value=%2.4f, errflag=%d\r\n", __func__, __LINE__, ModbusRegisterMap[modMap->first].rtype, ModbusRegisterMap[modMap->first].reg, RegisterValueMap[modMap->first].float_value, ret ); - } - } -}
--- a/ICE-Application/src/ModbusMaster/ModbusMaster.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -/****************************************************************************** - * - * File: ModbusMaster.h - * Desciption: interface for the ICE Modbus Master - * - *****************************************************************************/ -#ifndef MODBUS_MASTER_H -#define MODBUS_MASTER_H - -#include "global.h" - -#ifdef __cplusplus -#define EXTERNC extern "C" -#else -#define EXTERNC -#endif - -EXTERNC void ModbusMaster(void const *args); - -#undef EXTERNC - -#define SIZE_MB_BUFFER 50 -#define MB_BAUD_RATE 19200 - -typedef union mr_register_32_bit_float -{ - float f; - struct - { - uint16_t lo; - uint16_t hi; - }w; - struct - { - uint8_t lo_lo; - uint8_t lo_hi; - uint8_t hi_lo; - uint8_t hi_hi; - }b; -} MR_REGISTER_32_BIT_FLOAT; - -typedef union mr_register_32_bit_int -{ - int32_t i; - struct - { - uint16_t lo; - uint16_t hi; - }w; - struct - { - uint8_t lo_lo; - uint8_t lo_hi; - uint8_t hi_lo; - uint8_t hi_hi; - }b; -} MR_REGISTER_32BIT_INT; - -typedef union mr_register_32_bit_uint -{ - uint32_t i; - struct - { - uint16_t lo; - uint16_t hi; - }w; - struct - { - uint8_t lo_lo; - uint8_t lo_hi; - uint8_t hi_lo; - uint8_t hi_hi; - }b; -} MR_REGISTER_32BIT_UINT; - -typedef union mr_register_16_bit_int -{ - int16_t w; - struct - { - uint8_t lo; - uint8_t hi; - }b; -} MR_REGISTER_16BIT_INT; - -typedef union mr_register_16_bit_uint -{ - uint16_t w; - struct - { - uint8_t lo; - uint8_t hi; - }b; -} MR_REGISTER_16BIT_UINT; - -#define TYPE_32BIT_FLOAT 0 -#define TYPE_32BIT_INT 1 -#define TYPE_32BIT_UINT 2 -#define TYPE_16BIT_INT 3 -#define TYPE_16BIT_UINT 4 -#define TYPE_MULTI_BYTE 5 - -//Frame crc calucation -uint16_t modbus_crc(uint8_t* buf, int len); -void modbus_recv(); -void SendModbusCommand(uint8_t slave_address, uint16_t firstReg, uint16_t noRegs); -void modbus_init( uint16_t baudRate ); -bool mbIntComplete(); - -bool ReadModbus_32bit_float( float *float_value, int order, unsigned char *rd_buf ); -bool ReadModbus_32bit_int( int32_t *int32_value, int order, unsigned char *rd_buf ); - -bool WriteModbus_32bit_float( float float_value, int order, unsigned char *xmt_buf ); -bool WriteModbus_32bit_int( int32_t int32_value, int order, unsigned char *xmt_buf ); - -#endif
--- a/ICE-Application/src/ModbusMaster/ModbusMasterApi.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -#include "ModbusMasterApi.h" -#include "ICELog.h" -#include <stdio.h> - -bool ModbusMasterReadRegister( const std::string &io_tag, ModbusValue *value ) -{ - if( !(RegisterValueMap.count(io_tag)) ) { - printf("%s:%d: Key=%s not in Modbus Map\r\n", __func__, __LINE__, io_tag.c_str() ); - return false; - } - - value->value = RegisterValueMap[io_tag].float_value; - value->errflag = RegisterValueMap[io_tag].errflag; - return true; -} - -bool ModbusMasterWriteRegister( const std::string &io_tag, float value ) -{ - if( !(RegisterValueMap.count(io_tag)) ) { - printf("%s:%d: Key=%s not in Modbus Map\r\n", __func__, __LINE__, io_tag.c_str() ); - return false; - } - - logDebug("\r[DEBUG] writing tag %s\n", io_tag.c_str()); - RegisterValueMap[io_tag].float_value = value; - return true; -} - -bool ScriptWriteRegister( const char *io_tag, double value ) -{ - std::string ioString = io_tag; - if( !(RegisterValueMap.count(ioString)) ) { - printf("%s:%d: Key=%s not in Register Map\r\n", __func__, __LINE__, ioString.c_str() ); - return false; - } - - logDebug("\r[DEBUG] reading tag %s\n", io_tag); - - RegisterValueMap[ioString].float_value = value; - return true; -} - -double ScriptReadRegister( const char *io_tag ) -{ - std::string ioString = io_tag; - if( !(RegisterValueMap.count(ioString)) ) { - printf("%s:%d: Key=%s not in Register Map\r\n", __func__, __LINE__, ioString.c_str() ); - return -1; - } - - return RegisterValueMap[ioString].float_value; -} \ No newline at end of file
--- a/ICE-Application/src/ModbusMaster/ModbusMasterApi.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -#ifndef MODBUSMASTERAPI_H -#define MODBUSMASTERAPI_H - -#include <string> -#include "global.h" - -struct ModbusValue { - float value; - uint32_t errflag; -}; - -bool ModbusMasterReadRegister( const std::string &io_tag, ModbusValue *value ); -bool ModbusMasterWriteRegister( const std::string &io_tag, float value ); -void ModbusMasterExecCmd( ThreadName_t replyTo, char *cmd ); - -bool ScriptWriteRegister( const char *io_tag, double value ); -double ScriptReadRegister( const char *io_tag ); - -#endif -
--- a/ICE-Application/src/OutputTask/ControlClass/Control.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/****************************************************************************** - * - * File: Control.cpp - * Desciption: ICE Control Class implementation for Output Thread - * - *****************************************************************************/ -#include <Control.h> - -// -// method: getMappedState -// description: convert the control state into its analytics code -// the values here should map to the RELAY_STATUS_* codes -// define in global.h -// -// @param none -// @return relay-status code -// -int Control::getMappedState(void) const -{ - switch (controlType) { - case CONTROL_TIMER: - case CONTROL_SETPOINT: - // automatic control - return (state) ? RELAY_STATUS_AUTO_ON : RELAY_STATUS_AUTO_OFF; - case CONTROL_MANUAL: - // manual control - return (state) ? RELAY_STATUS_MANUAL_ON : RELAY_STATUS_MANUAL_OFF; - case CONTROL_FAILSAFE: - // failsafe control - return (state) ? RELAY_STATUS_FAILSAFE_ON : RELAY_STATUS_FAILSAFE_OFF; - case CONTROL_COMPOSITE: - // FIXME! This is a hack to get the correct mapped relay status - if ( input.compare(0, strlen("i_flowsw"), "i_flowsw") == 0 ) { - return ( state ) ? RELAY_STATUS_FLOW_FAILSAFE_ON : RELAY_STATUS_FLOW_FAILSAFE_OFF; - } else { - return ( state ) ? RELAY_STATUS_AUTO_ON : RELAY_STATUS_AUTO_OFF; - } - case CONTROL_PID: - // not implememented - default: - return -1; - } -}
--- a/ICE-Application/src/OutputTask/ControlClass/Control.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/****************************************************************************** - * - * File: Control.h - * Desciption: interface file for the ICE Output Task Control - * - * Controls are sorted by priority on the output - * vector. The highest priority control manipulates - * the output via Modbus writes. - * - *****************************************************************************/ -#ifndef CONTROL_H -#define CONTROL_H - -#include <stdio.h> -#include <string> - -#include "OutputTask.h" -#include "global.h" - - -//! Control - implements a control (for use with the Output thread) -class Control -{ -private: - std::string id; // control identifier - Control_t controlType; // control type - std::string input; // input - unsigned int priority; // control priority - ControlState state; // state (ON or OFF) - -public: - /// creates a control instance - Control(std::string id, - Control_t controlType, - std::string input, - unsigned int priority, - ControlState state) : - id(id), controlType(controlType), input(input), priority(priority), state(state) {} - /// destroys a control instance - ~Control() { - //printf("\r%s invoked\n", __func__); - } - - /// display the pertinent data of a Control instance - void display() { - printf("[%-24s | %3s | %u] -> ", - id.c_str(), (state == CONTROL_ON) ? "ON" : "OFF", priority); - } - - std::string getId() const { - return id; - } - - Control_t getControlType(void) const { - return controlType; - } - - std::string getInput(void) const { - return input; - } - - unsigned int getPriority() const { - return priority; - } - - ControlState getState(void) const { - return state; - } - - int getMappedState(void) const; - - void setState(ControlState _state) { - state = _state; - } -}; - -#endif \ No newline at end of file
--- a/ICE-Application/src/OutputTask/OutputTask.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,545 +0,0 @@ -/****************************************************************************** - * - * File: OutputTask.cpp - * Desciption: source for the ICE Output task - * - *****************************************************************************/ -#include <vector> -#include <string> -#include <algorithm> -#include <assert.h> -#include <time.h> -#include "OutputTask.h" -#include "global.h" -#include "cJSON.h" -#include "ModbusMasterApi.h" -#include "LoggerApi.h" -#include "Control.h" -#include "rtc.h" -#include "ICELog.h" - -//#include "../add-ons/MTSLog/MTSLog.h" - - -// local functions -static int createOutput(const char *controlFile); -static int loadPersistentOutputs(void); -static void writeOutputs(const std::string, const std::string); -static int enableOutputReqHandler(OutputControlMsg_t *msg); -static int disableOutputReqHandler(OutputControlMsg_t *msg); -static int unregisterControl(const char *id, unsigned int pri, const char *output); -static void dumpEventRecord(EventReasonStruct_t &ev); - -// -// The Output Map -// -// this is the main data structure used to distinguish which control has -// priority of an output. the layout is as-follows: -// -// outputMap["o_rly1"]-> Control<"ManControl_rly1", 100, ON > <-- highest pri control manipulates relay -// Control<"Flowswitch", 700 OFF> <-- lower pri (queued up) -// Control<"SetpointControl_rly1", 800, OFF> <-- lowest pri (queued up) -// -// outputMap["o_rly2"]-> Control<"Flowswitch", 700, OFF> -// -> Control<"SetpointControl_rly2", 800, OFF> -// -// outputMap["o_rly3"]-> Control<"ManControl_rly3", 100, ON > -// Control<"Flowswitch", 700, ON > -// Control<"TimerControl_rly3", 750, ON > -// -// -// The Control Vector (per relay) is always sorted by priority, whereas -// the highest priority control (lower priority number) is at the front -// (v.front()) of the list. -// - -typedef std::map<std::string, std::vector<Control> > StringOutputVector_t; -static StringOutputVector_t outputMap; - -// operator for sorting the outputs vectors -bool operator<(const Control &control1, const Control &control2) -{ - return control1.getPriority() < control2.getPriority(); -} - -/***************************************************************************** - * Function: OutputTask - * Description: Main entry point for the Output Task - * - * @param args -> not used - * @return none - *****************************************************************************/ -void OutputTask(void const *args) -{ - int rc; - (void)args; - - printf("\r%s has started...\n", __func__); - -#ifdef LOAD_PERSISTENT_CONFIGURATIONS - if ( loadPersistentOutputs() != 0 ) { - logError("Failed to load persistent outputs"); - } -#endif - - // signal the main thread to continue - osSignalSet(mainThreadId, sig_config_continue); - - while (true) { - // wait for a message - osEvent evt = OutputMasterMailBox.get(); - if (evt.status == osEventMail) { - - OutputControlMsg_t *msg = (OutputControlMsg_t*) evt.value.p; - - switch ( msg->action ) { - case ACTION_NEW: - // read the file and and create an output entry - rc = createOutput(msg->controlFile); - if ( rc != 0 ) { - logError("%s: failed to create output %s\n", - __func__, msg->controlFile); - } - break; - case ACTION_CONTROL_ON: - logInfo("%s is requesting ON control of %s", msg->id, msg->output_tag); - rc = enableOutputReqHandler(msg); - if ( rc != 0 ) { - logError("%s: failed to enable output for %s", - __func__, msg->id); - } - break; - case ACTION_CONTROL_OFF: - logInfo("%s is requesting OFF control of %s", msg->id, msg->output_tag); - rc = disableOutputReqHandler(msg); - if ( rc != 0 ) { - logError("%s: failed to disable output for %s", - __func__, msg->id); - } - break; - case ACTION_CONTROL_UNREGISTER: - logInfo("%s is requesting its deletion from %s", msg->id, msg->output_tag); - rc = unregisterControl(msg->id, msg->priority, msg->output_tag); - if ( rc != 0 ) { - logError("%s: failed to unregister control %s", - __func__, msg->id); - } - break; - default: - logError("%s unknown action %u", __func__, msg->action); - break; - } - - // free the message - OutputMasterMailBox.free(msg); - - // refresh the outputs - writeOutputs(msg->id, msg->output_tag); - } - } -} - -/***************************************************************************** - * Function: publishEvent - * Description: publish an event to the logger - * - * @param output -> the output channel - * @param control -> the control, this can be null - * @return none - *****************************************************************************/ -void publishEvent(std::string output, const Control *control) -{ - - EventReasonStruct_t ev; - ModbusValue input_value; - ModbusValue output_value; - memset(&ev, 0, sizeof(ev)); - - - // if there's no control, that means the only control that was on - // the relay stack has been destroyed, so we'll send a NO CONTROL - // event code. - if ( !control ) { - ev.eventReason = EVENT_REASON_NO_CONTROL; - ModbusMasterReadRegister(output, &output_value); - strncpy(ev.outputTag, output.c_str(), sizeof(ev.outputTag)); - ev.outputValue = output_value.value; - dumpEventRecord(ev); - EventLoggerApi(ev); - return; - } - - switch ( control->getControlType() ) { - case CONTROL_SETPOINT: - case CONTROL_COMPOSITE: - ev.eventReason = EVENT_REASON_AUTO; - strncpy(ev.inputTag, control->getInput().c_str(), sizeof(ev.inputTag)); - strncpy(ev.outputTag, output.c_str(), sizeof(ev.outputTag)); - ModbusMasterReadRegister(control->getInput(), &input_value); - ModbusMasterReadRegister(output, &output_value); - ev.inputValue = input_value.value; - ev.outputValue = output_value.value; - dumpEventRecord(ev); - EventLoggerApi(ev); - break; - - case CONTROL_MANUAL: - ev.eventReason = EVENT_REASON_MANUAL; - strncpy(ev.outputTag, output.c_str(), sizeof(ev.outputTag)); - ModbusMasterReadRegister(output, &output_value); - ev.outputValue = output_value.value; - dumpEventRecord(ev); - EventLoggerApi(ev); - break; - - case CONTROL_TIMER: - ev.eventReason = EVENT_REASON_TIMER; - strncpy(ev.outputTag, output.c_str(), sizeof(ev.outputTag)); - ModbusMasterReadRegister(output, &output_value); - ev.outputValue = output_value.value; - dumpEventRecord(ev); - EventLoggerApi(ev); - break; - - case CONTROL_FAILSAFE: - case CONTROL_SENSOR_ERROR: - ev.eventReason = EVENT_REASON_FAILSAFE; - strncpy(ev.inputTag, control->getInput().c_str(), sizeof(ev.inputTag)); - strncpy(ev.outputTag, output.c_str(), sizeof(ev.outputTag)); - ModbusMasterReadRegister(control->getInput(), &input_value); - ModbusMasterReadRegister(output, &output_value); - ev.inputValue = input_value.value; - ev.outputValue = output_value.value; - dumpEventRecord(ev); - EventLoggerApi(ev); - break; - - default: - logError("%s: unknown control type\n", __func__); - break; - } -} - -/***************************************************************************** - * Function: writeOutputs - * Description: send a message to the modbus master of who's in control - * - * - * @param[in] id -> control identifier - * @param[in] output_tag -> the output to write - * @return none - *****************************************************************************/ -static void writeOutputs(const std::string id, const std::string output_tag) -{ - (void) id; - - if ( output_tag.empty() ) { - logError("%s: invalid output tag", __func__); - return; - } - - StringOutputVector_t::const_iterator pos; - - // find the output - pos = outputMap.find(output_tag); - if ( pos != outputMap.end() ) { - if ( pos->second.empty() ) { - // we found the output but nothing's controlling it... - ModbusMasterWriteRegister(pos->first, RELAY_STATUS_NOT_CONTROLLED); - publishEvent(pos->first, NULL); - } else { - // get the mapped state for the highest priority control - int mappedState = pos->second.begin()->getMappedState(); - - // read the register value for this output - ModbusValue value; - ModbusMasterReadRegister(pos->first, &value); - - // a new higher priority control is manipulating the output, - // so send the new values to the modbus and publish an event - if ( value.value != mappedState ) { - ModbusMasterWriteRegister(pos->first, mappedState); - Control t = *(pos->second.begin()); - publishEvent(pos->first, &t); - } - } - // TODO: This may be a virtual output, so we'll need to see if it - // exists in the vregmap - } else { - logError("%s failed to find the selected output %s", - __func__, output_tag.c_str()); - } -} - -/***************************************************************************** - * Function: createOutput - * Description: - * - * @param outputFile -> name of output file - * @return none - *****************************************************************************/ -static int createOutput(const char *outputFile) -{ - char dataBuf[MAX_FILE_SIZE]; - bool status = GLOBAL_mdot->readUserFile(outputFile, (void *)dataBuf, sizeof(dataBuf)); - if ( status != true ) { - logError("%s failed to read %s", __func__, outputFile); - return -1; - } - - cJSON * root = cJSON_Parse(dataBuf); - if ( !cJSON_HasObjectItem(root, "id") ) { - logError("%s: error extracting expected tags", __func__); - cJSON_Delete(root); - return -1; - } - - std::string id = cJSON_GetObjectItem(root,"id")->valuestring; - cJSON_Delete(root); - - std::vector<Control> v; - outputMap[id] = v; - - return 0; -} - -/***************************************************************************** - * Function: enableOutputReqHandler - * Description: handle a request to enable an output - * - * @param[in] msg -> the message request - * @return -1 on error - *****************************************************************************/ -static int enableOutputReqHandler(OutputControlMsg_t *msg) -{ - // attempt to find the output in the map - StringOutputVector_t::iterator pos; - - pos = outputMap.find(msg->output_tag); - if ( pos == outputMap.end() ) { - logError("%s: failed to find the designated output %s\n", - __func__, msg->output_tag); - return -1; - } - - if ( pos->second.empty() ) { - // this is a new request - std::string cid(msg->id); - std::string input(msg->input_tag); - Control c(cid, msg->controlType, input, msg->priority, CONTROL_ON); - pos->second.push_back(c); - } else { - // find this control in the list - std::vector<Control>::iterator v; - for ( v = pos->second.begin(); v != pos->second.end(); ++v ) { - if ( strcmp(v->getId().c_str(), msg->id) == 0 ) { - v->setState(CONTROL_ON); - break; - } - } - if ( v == pos->second.end() ) { - // this is a new request, so add it and sort the vector - std::string cid(msg->id); - std::string input(msg->input_tag); - Control c(cid, msg->controlType, input, msg->priority, CONTROL_ON); - pos->second.push_back(c); - std::sort(pos->second.begin(), pos->second.end()); - } - } - - return 0; -} - -/***************************************************************************** - * Function: disableOutputReqHandler - * Description: handle a request to disable an output - * - * @param[in] msg -> the message request - * @return none - *****************************************************************************/ -static int disableOutputReqHandler(OutputControlMsg_t *msg) -{ - // attempt to find the output in the map - StringOutputVector_t::iterator pos; - - pos = outputMap.find(msg->output_tag); - if ( pos == outputMap.end() ) { - logError("%s: failed to find the designated output %s\n", - __func__, msg->output_tag); - return -1; - } - - // if the control list is empty, push this control on the list - if ( pos->second.empty() ) { - std::string cid(msg->id); - std::string input(msg->input_tag); - Control c(cid, msg->controlType, input, msg->priority, CONTROL_OFF); - pos->second.push_back(c); - } else { - // find this control in the list - std::vector<Control>::iterator v; - for ( v = pos->second.begin(); v != pos->second.end(); ++v ) { - if ( strcmp(v->getId().c_str(), msg->id) == 0 ) { - v->setState(CONTROL_OFF); - break; - } - } - - if ( v == pos->second.end() ) { - // this is a new request, so add it and sort the vector - std::string cid(msg->id); - std::string input(msg->input_tag); - Control c(cid, msg->controlType, input, msg->priority, CONTROL_OFF); - pos->second.push_back(c); - std::sort(pos->second.begin(), pos->second.end()); - } - } - - return 0; -} - -/***************************************************************************** - * Function: unregisterControl - * Description: - * - * @param id -> control identifier - * @param pri -> priority - * @param output -> output (e.g. "o_rly05") - * - * @return 0 on success; -1 on error - *****************************************************************************/ -static int unregisterControl(const char *id, unsigned int pri, const char *output) -{ - // attempt to find the output in the map - StringOutputVector_t::iterator pos; - bool found = false; - - pos = outputMap.find(output); - if ( pos == outputMap.end() ) { - logError("%s: failed to find the designated output %s\n", - __func__, output); - return -1; - } - - // find the control in the list - std::vector<Control>::iterator v; - for ( v = pos->second.begin(); v != pos->second.end(); ++v) { - if ( strcmp(v->getId().c_str(), id) == 0 ) { - pos->second.erase(v); - found = true; - break; - } - } - - if ( !found ) { - logError("%s: failed to find control %s in list", __func__, id); - return -1; - } - return 0; -} - -/***************************************************************************** - * Function: loadPersistentOutputs - * Description: build the output map - * - * @param none - * @return 0 on success; -1 of failure - *****************************************************************************/ -static int loadPersistentOutputs(void) -{ - bool status; - cJSON *root; - int rc = 0; - - printf("\rLoading persistent outputs:\n"); - - std::vector<std::string> file_list = GLOBAL_mdot->listUserFiles(); - - for (std::vector<std::string>::const_iterator i = file_list.begin(); i != file_list.end(); ++i) { - // load in all of the output files - if( strncmp( i->c_str(), OUTPUT_STR, strlen(OUTPUT_STR)) == 0 || - strncmp( i->c_str(), VOUTPUT_STR, strlen(VOUTPUT_STR)) == 0) { - char scratchBuf[MAX_FILE_SIZE]; - - status = GLOBAL_mdot->readUserFile(i->c_str(), scratchBuf, MAX_FILE_SIZE); - if( status != true ) { - logError("%s: failed to read %s", __func__, i->c_str()); - rc = -1; - } else { - logInfo("%s: successfully read %s", __func__, i->c_str()); - } - - root = cJSON_Parse(scratchBuf); - std::string id = cJSON_GetObjectItem(root,"id")->valuestring; - printf("\r output %s loaded\n", i->c_str()); - cJSON_Delete(root); - - // emplace the empty control vector into the output map - std::vector<Control> v; - outputMap[id] = v; - } - } - return rc; -} - -/***************************************************************************** - * Function: dumpEventRecord - * Description: display the contents of an event record - * - * @param[in] ev -> the event structure - * @return none - *****************************************************************************/ -static void dumpEventRecord(EventReasonStruct_t &ev) -{ - const char *mapper[] = { "auto", - "manual", - "timer", - "flow", - "failsafe", - "no control" - }; - - time_t rawtime; - time(&rawtime); - - int iyr=0, imo=0, idy=0, ihr=0, imn=0, isc=0; -#undef TODO_ICE -#if 0 - rtc_get_time(&iyr, &imo, &idy, &ihr, &imn, &isc); -#endif - - printf("\rEVENT RECORD (%04d-%02d-%02d %02d:%02d:%02d)\r\n", - iyr, imo, idy, ihr, imn, isc); - printf("\rev.eventReason = %d (%s)\n", ev.eventReason, mapper[ev.eventReason]); - printf("\rev.inputTag = %s\n", ev.inputTag[0] ? ev.inputTag : "empty"); - printf("\rev.outputTag = %s\n", ev.outputTag[0] ? ev.outputTag : "empty"); - printf("\rev.inputValue = %.02f\n", ev.inputValue); - printf("\rev.outputValue = %.02f (%s)\n", ev.outputValue, - ((unsigned int)ev.outputValue & 1 == 1) ? "on" : "off"); -} - -/***************************************************************************** - * Function: DisplayOutputs - * Description: Display a list of outputs and their controls - * - * @param none - * @return none - *****************************************************************************/ -void DisplayOutputs(void) -{ - StringOutputVector_t::iterator pos; - - for ( pos = outputMap.begin(); pos != outputMap.end(); ++pos ) { - if ( pos->second.empty() ) { - printf("\r [%s]-> [no controls] \n", pos->first.c_str()); - } else { - printf("\r [%s]-> ", pos->first.c_str()); - std::vector<Control>::iterator i; - for ( i = pos->second.begin(); i != pos->second.end(); ++i ) { - i->display(); - } - printf("\n"); - } - } - printf("\r\n"); -}
--- a/ICE-Application/src/OutputTask/OutputTask.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -#ifndef OUTPUTTASK_H -#define OUTPUTTASK_H - -#ifdef __cplusplus -#define EXTERNC extern "C" -#else -#define EXTERNC -#endif - -// thread prototype -EXTERNC void OutputTask(void const *args); - -#undef EXTERNC - -// FIXME: think about analog! -typedef enum { - CONTROL_OFF = 0, - CONTROL_ON = 1 -} ControlState; - -// public functions -void DisplayOutputs(void); - -#define RELAY_STATUS_AUTO_ON 129 // automatic control ON -#define RELAY_STATUS_AUTO_OFF 128 // automatic control OFF -#define RELAY_STATUS_MANUAL_ON 65 // manual control ON -#define RELAY_STATUS_MANUAL_OFF 64 // manual control OFF -#define RELAY_STATUS_FAILSAFE_ON 33 // general failsafe duty ON -#define RELAY_STATUS_FAILSAFE_OFF 32 // general failsafe duty OFF -#define RELAY_STATUS_FORCED_OFF 16 // ?? -#define RELAY_STATUS_FLOW_FAILSAFE_ON 9 // flow failsafe control ON -#define RELAY_STATUS_FLOW_FAILSAFE_OFF 8 // flaw failsafe control OFF -#define RELAY_STATUS_NOT_CONTROLLED 0 // no controls - -#define OUTPUT_STR "output_" -#define VOUTPUT_STR "voutput_" -#endif
--- a/ICE-Application/src/Utilities/timerUtils.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1872 +0,0 @@ -/************************************************************************ - * * - * Copyright (c) 2012 Nalco Company. All Rights Reserved. * - * * - ************************************************************************/ - -/* - * Scheduling Utilities and testing support functions - */ - -#include "timerUtils.h" -#include <time.h> -#include "rtos.h" - - /************************************************************************ - * * - * Copyright (c) 2012 Nalco Company. All Rights Reserved. * - * * - ************************************************************************/ - -// local implementation of localtime_r, not supported on IAR -Mutex localtimeMutex; - -/* index with month */ -static int dayTable[2][13] = -{/* J F M A M J J A S O N D */ -{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, -{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; - - -int UTgetDaysInMonth(int year, int month) -{ - int leap; - if ( year < 2000 || month < 1 || month > 12 ) - { - return 0; - } - leap = (( year % 4 == 0 ) && ( year % 100 != 0 )) || ( year % 400 == 0 ); - - return ( dayTable[leap][month] ); -} - - -bool timer_debug_print = false; -bool timer_debug_kludge_data = false; -unsigned long time_data_kludge_chkpt_time = 0; -unsigned long time_data_kludge_boot_time = 0; - -unsigned char Util_computeDownMask( int begin_wday, int end_wday ); -bool TSCutilEveryWeekMissed( CNTL_SCHED_STRUCT *pElem, unsigned long chkpt_local_sec, unsigned long chkpt_spm, int chkpt_wday, unsigned long boot_local_sec, unsigned long boot_spm, int boot_wday, unsigned long sched_start, unsigned long sched_end, TSC_SCHED_MISS_REASON *pReason, int *pMisses, int *pStartToEndDiff ); -bool TSCutilEveryWeekMissedV2( CNTL_SCHED_STRUCT *pElem, unsigned long chkpt_local_sec, unsigned long boot_local_sec, TSC_SCHED_MISS_REASON *pReason, int *pMisses, int *pStartToEndDiff ); -void Util_computeBeginEndDownForDay( int chkpt_wday, unsigned long chkpt_spm, int boot_wday, unsigned long boot_spm, int day, unsigned long *down_begin, unsigned long *down_end); - -static DAY_SCHEDULE dayMask[] = {DAY_SCHEDULE_SUNDAY_MASK, DAY_SCHEDULE_MONDAY_MASK, DAY_SCHEDULE_TUESDAY_MASK, DAY_SCHEDULE_WEDNESDAY_MASK, - DAY_SCHEDULE_THURSDAY_MASK, DAY_SCHEDULE_FRIDAY_MASK, DAY_SCHEDULE_SATURDAY_MASK }; - - -/* - * return the number of days in the schedule - */ -int Util_numDaysInSchedule( unsigned char dailySchedule ) -{ - int d, numOfDaysInSchedule; - - /* only get here if starting day was NOT SUNDAY */ - for ( d = SUNDAY, numOfDaysInSchedule = 0; d <= SATURDAY; ++d ) - { - if ( dailySchedule & dayMask[d] ) - { - ++numOfDaysInSchedule; - } - } - - return numOfDaysInSchedule; -} - -/* - * Util_findAday - * - * starting with (and including) the given day return the first day that is scheduled in the daily schedule - * the starting day is given as day of week which is the number of days past sunday, - * sunday is 0 and saturday is 6 - * - * the dailySchedule is based upon the DAY_SCHEDULE domain - * DAY_SCHEDULE_SUNDAY_MASK = 0x01 - * DAY_SCHEDULE_MONDAY_MASK = 0x02 - * DAY_SCHEDULE_TUESDAY_MASK = 0x04 - * DAY_SCHEDULE_WEDNESDAY_MASK = 0x08 - * DAY_SCHEDULE_THURSDAY_MASK = 0x10 - * DAY_SCHEDULE_FRIDAY_MASK = 0x20 - * DAY_SCHEDULE_SATURDAY_MASK = 0x40 - */ -int Util_findAday( int startingDay, unsigned char dailySchedule ) -{ - int d; - - if ( dailySchedule == 0 ) - { - //printf("dailySchedule is 0!\n"); - return 0; - } - - for ( d = startingDay; d <= SATURDAY; ++d ) - { - if ( dailySchedule & dayMask[d] ) - { - return d; - } - } - - /* only get here if starting day was NOT SUNDAY */ - for ( d = SUNDAY; d < startingDay; ++d ) - { - if ( dailySchedule & dayMask[d] ) - { - return d; - } - } - - /* should NOT get here */ - printf("Didn't find a day in the schedule(0x%x)", dailySchedule); - return 0; -} -#ifdef COMMENTOUT -int Util_findAday( int startingDay, unsigned char dailySchedule ) -{ - int d; - if ( dailySchedule == 0 ) - { - //printf("dailySchedule is 0!\n"); - return 0; - } - - for ( d = startingDay; d <= SATURDAY; ++d ) - { - if ( dailySchedule & (0x1 << d) ) - { - return d; - } - } - - /* only get here if starting day was NOT SUNDAY */ - for ( d = SUNDAY; d < startingDay; ++d ) - { - if ( dailySchedule & (0x1 << d) ) - { - return d; - } - } - - /* should NOT get here */ - //printf("Didn't find a day in the schedule!\n"); - return 0; -} -#endif - - -/* - * TSCutilCmpStartTimes( pA, pB ) - * - * < 0 schedule A preceeds schedule B - * == 0 schedules A and B overlap in some way - * > 0 schedule A follows schedule B - * - * Converting the start and end times to seconds past midnight greatly simplifies - * the time comparisons - * - */ -int TSCutilCmpStartTimes( TSC_SCHED_ELEM *pA, TSC_SCHED_ELEM *pB ) -{ - int aStart, aEnd, bStart, bEnd; /* seconds past midnight of the starting and ending times */ - - aStart = pA->schedule.startTime.hour * SECONDS_PER_HOUR; - aStart += pA->schedule.startTime.minute * SECONDS_PER_MINUTE; - aStart += pA->schedule.startTime.second; - - aEnd = pA->schedule.endTime.hour * SECONDS_PER_HOUR; - aEnd += pA->schedule.endTime.minute * SECONDS_PER_MINUTE; - aEnd += pA->schedule.endTime.second; - - bStart = pB->schedule.startTime.hour * SECONDS_PER_HOUR; - bStart += pB->schedule.startTime.minute * SECONDS_PER_MINUTE; - bStart += pB->schedule.startTime.second; - - bEnd = pB->schedule.endTime.hour * SECONDS_PER_HOUR; - bEnd += pB->schedule.endTime.minute * SECONDS_PER_MINUTE; - bEnd += pB->schedule.endTime.second; - -#ifdef COMMENTOUT - printf("\t%s: StartTimes aHr(%ld),aMin(%ld), aSec(%ld), bHr(%ld), bMin(%ld), bSec(%ld)\n", __FUNCTION__, - pA->schedule.startTime.hour, pA->schedule.startTime.minute, pA->schedule.startTime.second, - pB->schedule.startTime.hour, pB->schedule.startTime.minute, pB->schedule.startTime.second); - - printf("\t%s: EndTimes aHr(%ld),aMin(%ld), aSec(%ld), bHr(%ld), bMin(%ld), bSec(%ld)\n",__FUNCTION__, - pA->schedule.endTime.hour, pA->schedule.endTime.minute, pA->schedule.endTime.second, - pB->schedule.endTime.hour, pB->schedule.endTime.minute, pB->schedule.endTime.second); - - printf("\t%s: aStart(%d), aEnd(%d)\n", __FUNCTION__,aStart, aEnd); - printf("\t%s: bStart(%d), bEnd(%d)\n", __FUNCTION__,bStart, bEnd); -#endif - - if ( aStart > bStart ) - { - if ( aStart > bEnd ) - { - return 1; - } - return 0; - } - - if ( aStart < bStart ) - { - if ( aEnd < bStart ) - { - return -1; - } - - return 0; - } - - return 0; - -} -/* - * Util_getMonthWeek - * - * return the month week for the given date - * - * THE GIVEN DATE MUST BE >= CURRENT DATE - * - * The algorithm requires the knowning the weekDay for the given monthDay. - * - */ - - -int Util_getMonthWeek( TSC_SCHED_ELEM *pSchedElem, CNTL_DATE_STRUCT *pDate ) -{ - time_t calTime; - struct tm brokenDownTime; - int dayOfWeek, dom, wom; - - calTime = pSchedElem->calTime; - brokenDownTime = pSchedElem->requestTime; - - /* verify the given date assertion */ - if ( brokenDownTime.tm_year > pDate->year || - ( brokenDownTime.tm_year == pDate->year && brokenDownTime.tm_mon > pDate->month) || - (brokenDownTime.tm_year == pDate->year && brokenDownTime.tm_mon == pDate->month && brokenDownTime.tm_mday < pDate->day) - ) - { - //printf("%s: Given date (%2.2ld/%2.2ld/%2.2ld) is in the past\n", __FUNCTION__, pDate->month, pDate->day, pDate->year); - return 0; - } - - /* get the day of week for the first day of the month */ - dayOfWeek = Util_getDoWThisMonth( pSchedElem, 1 ); - - /* the day of the month that was the last day in the first week */ - for ( dom = 7 - dayOfWeek, wom = 1; wom < 5; dom += 7, ++wom ) - { - if ( pDate->day <= dom ) - { - return wom; - } - } - - return ( WEEKLY_CHOICE_LAST_WEEK ); -} - - -/* - * Util_getDoWThisMonth - * - * Get the day of week for a given day of the month in the current month - * - * This function assumes the given day of month is in the current month - * the day of month computation is slightly different depending upon if the given - * day is before or after the current day of the month - */ -int Util_getDoWThisMonth( TSC_SCHED_ELEM *pSchedElem, int dom ) -{ - time_t calTime; - struct tm brokenDownTime; - int dayOfWeek, dayDiff; - - calTime = pSchedElem->calTime; - brokenDownTime = pSchedElem->requestTime; - - if ( dom < 1 || dom > 31 ) - { - return 0; - } - - if ( dom == brokenDownTime.tm_mday ) - { - return brokenDownTime.tm_wday; - } - - /* compute day of month for a day following today */ - if ( dom > brokenDownTime.tm_mday ) - { - dayDiff = dom - brokenDownTime.tm_mday; - dayOfWeek = (brokenDownTime.tm_wday + dayDiff) % 7; - - return dayOfWeek; - } - - /* compute day of month for a day preceeding the current day */ - - dayDiff = ( brokenDownTime.tm_mday - dom ) % 7; - dayOfWeek = ((7 - dayDiff) + brokenDownTime.tm_wday) % 7; - - return dayOfWeek; -} - -/* - * int Util_dateTimeDiff( CNTL_SCHED_STRUCT *pDateTime1, CNTL_SCHED_STRUCT *pDateTime2, long *pDiffInSeconds ) - * - * Return - * OK and the difference in seconds of dateTime1, dateTime2 in pDiffInSeconds - * ERROR if any paramter pointer is NULL or dateTime1 > dateTime2 - * - */ -int Util_dateTimeDiff( CNTL_SCHED_STRUCT *pDateTime1, CNTL_SCHED_STRUCT *pDateTime2, unsigned long *pDiffInSeconds ) -{ - DMSTRING128 errorString; - int chk = ERROR; - time_t calTime; - struct tm brokenDownTime; - int days, hours, minutes, seconds, totalSeconds, m, daysInMonth; - - do - { - if ( pDateTime1 == NULL || pDateTime2 == NULL || pDiffInSeconds == NULL ) - { - //printf("bad params"); - snprintf(errorString, sizeof(DMSTRING128),"%s: NULL parameter, pDateTime1(%p), pDateTime2(%p) pDiffInSeconds(%p)", - __FUNCTION__, pDateTime1, pDateTime2, pDiffInSeconds ); - break; - } - - /* - * For testing purposes - */ -#ifdef COMMENTOUT - printf(" Date1(%2.2ld/%2.2ld/%4.4ld),Time1(%2.2ld:%2.2ld)\n", - pDateTime1->startDate.month, pDateTime1->startDate.day, pDateTime1->startDate.year, - pDateTime1->startTime.hour, pDateTime1->startTime.minute); - - printf(" Date2(%2.2ld/%2.2ld/%4.4ld),Time2(%2.2ld:%2.2ld)\n\n", - pDateTime2->startDate.month, pDateTime2->startDate.day, pDateTime2->startDate.year, - pDateTime2->startTime.hour, pDateTime2->startTime.minute); -#endif - /* verify dateTime1 <= dateTime2 */ - - if ( pDateTime1->startDate.year > pDateTime2->startDate.year ) - { - //printf("bad year"); - snprintf(errorString, sizeof(DMSTRING128),"%s: dateTime1 year(%ld) > dateTime2 year(%ld)", - __FUNCTION__, pDateTime1->startDate.year, pDateTime2->startDate.year ); - break; - } - if ( (pDateTime1->startDate.year + 1) < pDateTime2->startDate.year ) - { - //printf("bad year"); - snprintf(errorString, sizeof(DMSTRING128),"%s: dateTime2 year(%ld) >= dateTime1 + 1 (%ld)", - __FUNCTION__, pDateTime2->startDate.year, pDateTime1->startDate.year ); - break; - } - if ( pDateTime1->startDate.year == pDateTime2->startDate.year ) - { - if ( pDateTime1->startDate.month > pDateTime2->startDate.month ) - { - //printf("bad month"); - snprintf(errorString, sizeof(DMSTRING128),"%s: dateTime1 month(%ld) > dateTime2 month(%ld)", - __FUNCTION__, pDateTime1->startDate.month, pDateTime2->startDate.month ); - break; - } - if ( pDateTime1->startDate.month == pDateTime2->startDate.month ) - { - if ( pDateTime1->startDate.day > pDateTime2->startDate.day ) - { - //printf("bad day"); - snprintf(errorString, sizeof(DMSTRING128),"%s: dateTime1 day(%ld) > dateTime2 day(%ld)", - __FUNCTION__, pDateTime1->startDate.day, pDateTime2->startDate.day ); - break; - } - if ( pDateTime1->startDate.day == pDateTime2->startDate.day ) - { - if ( pDateTime1->startTime.hour > pDateTime2->startTime.hour ) - { - //printf("bad hour"); - snprintf(errorString, sizeof(DMSTRING128),"%s: dateTime1 hour(%ld) > dateTime2 hour(%ld)", - __FUNCTION__, pDateTime1->startTime.hour, pDateTime2->startTime.hour ); - break; - } - if ( pDateTime1->startTime.hour == pDateTime2->startTime.hour ) - { - if ( pDateTime1->startTime.minute > pDateTime2->startTime.minute ) - { - //printf("bad minute"); - snprintf(errorString, sizeof(DMSTRING128),"%s: dateTime1 minute(%ld) > dateTime2 minute(%ld)", - __FUNCTION__, pDateTime1->startTime.minute, pDateTime2->startTime.minute ); - break; - } - } - } - } - } - - /* all the checks pass so */ - chk = OK; - break; - - } while ( 0 ); - - if ( chk == ERROR ) - { - printf("%s", errorString ); - return chk; - } - - /* - * all the checks pass so on to computing the difference - * only the seconds past the hour are used from brokenDownTime, all other time - * attributes come from the two parameters. The seconds is a bit of a fudge - * implying DateTime1 is the current date/Time - */ - calTime = time( NULL ); - localtime_r( &calTime, &brokenDownTime ); /* need to know the seconds past the minute */ - - days = hours = minutes = seconds = totalSeconds = 0; - - if ( pDateTime1->startDate.year < pDateTime2->startDate.year ) - { - /* accumulate days */ - if ( pDateTime1->startDate.month < 12 ) - { - for ( m = pDateTime1->startDate.month + 1; m <= 12; ++m ) - { - days += UTgetDaysInMonth(pDateTime1->startDate.year, m); - } - } - /* days to end of month */ - daysInMonth = UTgetDaysInMonth(pDateTime1->startDate.year, pDateTime1->startDate.month); - if ( pDateTime1->startDate.day < daysInMonth ) - { - days += daysInMonth - pDateTime1->startDate.day ; - } - - /* days in time2 year */ - for ( m = 1; m < pDateTime2->startDate.month; ++m ) - { - days += UTgetDaysInMonth(pDateTime2->startDate.year, m); - } - - /* days up to start day in time2 year */ - days += pDateTime2->startDate.day - 1; - - /* accumulate the hours and minutes */ - hours = HOURS_PER_DAY - (pDateTime1->startTime.hour + 1); - minutes = MINUTES_PER_HOUR - (pDateTime1->startTime.minute + 1); - - hours += pDateTime2->startTime.hour; - minutes += pDateTime2->startTime.minute; - - totalSeconds = days * SECONDS_PER_DAY; - totalSeconds += hours * SECONDS_PER_HOUR; - totalSeconds += minutes * SECONDS_PER_MINUTE; - totalSeconds += SECONDS_PER_MINUTE - brokenDownTime.tm_sec; - *pDiffInSeconds = totalSeconds; - - return OK; - } - - /* - * Same Year different months - */ - if ( pDateTime1->startDate.month < pDateTime2->startDate.month ) - { - for ( m = pDateTime1->startDate.month + 1; m < pDateTime2->startDate.month; ++m ) - { - days += UTgetDaysInMonth(pDateTime1->startDate.year, m); - } - - daysInMonth = UTgetDaysInMonth(pDateTime1->startDate.year, pDateTime1->startDate.month); - if ( pDateTime1->startDate.day < daysInMonth ) - { - days += daysInMonth - pDateTime1->startDate.day ; - } - days += pDateTime2->startDate.day - 1; - - /* accumulate the hours and minutes */ - hours = HOURS_PER_DAY - (pDateTime1->startTime.hour + 1); - minutes = MINUTES_PER_HOUR - (pDateTime1->startTime.minute + 1); - - hours += pDateTime2->startTime.hour; - minutes += pDateTime2->startTime.minute; - - totalSeconds = days * SECONDS_PER_DAY; - totalSeconds += hours * SECONDS_PER_HOUR; - totalSeconds += minutes * SECONDS_PER_MINUTE; - totalSeconds += SECONDS_PER_MINUTE - brokenDownTime.tm_sec; - *pDiffInSeconds = totalSeconds; - - return OK; - } - - /* - * Same Year, month - */ - if ( pDateTime1->startDate.day < pDateTime2->startDate.day ) - { - days = pDateTime2->startDate.day - (pDateTime1->startDate.day + 1); - - /* accumulate the hours and minutes */ - hours = HOURS_PER_DAY - (pDateTime1->startTime.hour + 1); - minutes = MINUTES_PER_HOUR - (pDateTime1->startTime.minute + 1); - - hours += pDateTime2->startTime.hour; - minutes += pDateTime2->startTime.minute; - - totalSeconds = days * SECONDS_PER_DAY; - totalSeconds += hours * SECONDS_PER_HOUR; - totalSeconds += minutes * SECONDS_PER_MINUTE; - totalSeconds += SECONDS_PER_MINUTE - brokenDownTime.tm_sec; - *pDiffInSeconds = totalSeconds; - - return OK; - } - - /* - * Same Year, month, day - */ - if ( pDateTime1->startTime.hour < pDateTime2->startTime.hour ) - { - hours = pDateTime2->startTime.hour - (pDateTime1->startTime.hour + 1); - minutes = MINUTES_PER_HOUR - (pDateTime1->startTime.minute + 1); - - minutes += pDateTime2->startTime.minute; - - totalSeconds += hours * SECONDS_PER_HOUR; - totalSeconds += minutes * SECONDS_PER_MINUTE; - totalSeconds += SECONDS_PER_MINUTE - brokenDownTime.tm_sec; - *pDiffInSeconds = totalSeconds; - - return OK; - - } - - /* - * Same Year, month, day, hour, given the dateTime check above the time1 minute < time2 minute - */ - - minutes = pDateTime2->startTime.minute - (pDateTime1->startTime.minute + 1); - - totalSeconds += minutes * SECONDS_PER_MINUTE; - totalSeconds += SECONDS_PER_MINUTE - brokenDownTime.tm_sec; - *pDiffInSeconds = totalSeconds; - - return OK; -} - - - -/* - * TSCutilComputeStartDate - * - * Depending upon the specified schedule: - * start time, daily schedule, weekly schedule, the start date may move from today. - * - * Scheduling falls into 3 buckets - * One Shot - * Every Week or Every Other Week - * Specific Week of the Month - */ -void TSCutilComputeStartDate( TSC_SCHED_ELEM *pSchedElem ) -{ - switch ( pSchedElem->schedule.weeklySchedule ) - { - case WEEKLY_CHOICE_FIRST_WEEK: - case WEEKLY_CHOICE_SECOND_WEEK: - case WEEKLY_CHOICE_THIRD_WEEK: - case WEEKLY_CHOICE_FOURTH_WEEK: - case WEEKLY_CHOICE_LAST_WEEK: - TSCutilScheduleOneWeekPerMonth( pSchedElem ); - break; - - case WEEKLY_CHOICE_EVERY_WEEK: - case WEEKLY_CHOICE_EVERYOTHER_WEEK: - TSCutilScheduleWithInWeek( pSchedElem ); - break; - - default: - printf("%s: Unknown Weekly Schedule (%d) Specified\n", __FUNCTION__, pSchedElem->schedule.weeklySchedule); - break; - } -} - - -/* - * TSCutilScheduleWithInWeek - * - * update the CNTL_SCHED_STRUCT with the start date - * - * Based upon the selected day and start time the start day may move - * - * return the seconds to start date and time in addition to update the date - * - * Look for schedules that specify multiple days yet to come - * - */ -void TSCutilScheduleWithInWeek( TSC_SCHED_ELEM *pSchedElem ) -{ - struct tm brokenDownTime, newDate; - int d, day, anotherDay, newMonth, newDay, newYear, seconds, minutes, hours, totalSeconds; - int currentTimeInSeconds, scheduleTimeInSeconds; - time_t calTime; - - brokenDownTime = pSchedElem->requestTime; - calTime = pSchedElem->calTime; - - /* find the first day of the week in the schedule, starting from today */ - day = Util_findAday( brokenDownTime.tm_wday, pSchedElem->schedule.dailySchedule ); - - //printf("\t%s: sched_day(%d), today(%d)\n", __FUNCTION__, day, brokenDownTime.tm_wday); - - if ( brokenDownTime.tm_wday == day ) - { - //printf("\t%s: today is in schedule\n", __FUNCTION__); - /* - * Today is in the schedule if the start time has passed (hour, minute, second) - * then schedule for next week otherwise a schedule change is not required - * compute the seconds past midnight and compare with the start time (in seconds past midnight) - * All schedules start on the minute - */ - scheduleTimeInSeconds = pSchedElem->schedule.startTime.hour * SECONDS_PER_HOUR; - scheduleTimeInSeconds += pSchedElem->schedule.startTime.minute * SECONDS_PER_MINUTE; - - currentTimeInSeconds = brokenDownTime.tm_hour * SECONDS_PER_HOUR; - currentTimeInSeconds += brokenDownTime.tm_min * SECONDS_PER_MINUTE; - currentTimeInSeconds += brokenDownTime.tm_sec; - - if ( currentTimeInSeconds < scheduleTimeInSeconds ) - { - /* the scheduled time has not passed so no schedule change is required */ - //printf("\t%s: start time yet to occur no schedule change required\n", __FUNCTION__); - return; - } - - //printf("\t%s: start time has passed schedule change required\n", __FUNCTION__); - /* - * Today is on the schedule but the start time has passed. - * Is there a day past today on the schedule - */ - anotherDay = (day + 1) % 7; - anotherDay = Util_findAday( anotherDay, pSchedElem->schedule.dailySchedule ); - - //printf("\t%s: next scheduled day(%d)\n", __FUNCTION__, anotherDay); - - if ( anotherDay == day ) - { - //printf("\t%s: schedule for next week\n", __FUNCTION__); - /* - * There is only one day in the schedule so schedule same day next week - */ - /* add in the time from now to end of day */ - hours = 24 - (brokenDownTime.tm_hour + 1); - minutes = 60 - (brokenDownTime.tm_min + 1); - seconds = 60 - brokenDownTime.tm_sec; - totalSeconds = SECONDS_PER_HOUR * hours; - totalSeconds += SECONDS_PER_MINUTE * minutes; - totalSeconds += seconds; - - /* - * totalSeconds moves the clock to midnight tomorrow - * counting from tomorrow there are 6 days to the next scheduled day (anotherDay) - * If the weekly schedule is every other week an additional 7 days is added - */ - if ( pSchedElem->schedule.weeklySchedule == WEEKLY_CHOICE_EVERYOTHER_WEEK ) - { - totalSeconds += SECONDS_PER_DAY * 7; - } - - totalSeconds += SECONDS_PER_DAY * 6; - - calTime += totalSeconds; - localtime_r( &calTime, &newDate ); - - newDay = newDate.tm_mday; - newMonth = newDate.tm_mon + 1; - newYear = (newDate.tm_year - 100) + 2000; - - pSchedElem->schedule.startDate.year = newYear; - pSchedElem->schedule.startDate.month = newMonth; - pSchedElem->schedule.startDate.day = newDay; - - return; - } - - /* - * The starting day is not today - * add in the time from now to end of day - * accumulate full days seconds from the day after today to starting day - */ - //printf("\t%s: schedule change move date forward\n", __FUNCTION__); - - hours = 24 - (brokenDownTime.tm_hour + 1); - minutes = 60 - (brokenDownTime.tm_min + 1); - seconds = 60 - brokenDownTime.tm_sec; - totalSeconds = SECONDS_PER_HOUR * hours; - totalSeconds += SECONDS_PER_MINUTE * minutes; - totalSeconds += seconds; - - //printf("\t%s: seconds to midnight(%ld)\n", __FUNCTION__, totalSeconds); - - /* count days from today until first day in schedule */ - //printf("\t%s: anotherDay(%d) brokenDownTime.tm_wday(%d)\n", __FUNCTION__, anotherDay, brokenDownTime.tm_wday ); - - if ( anotherDay > brokenDownTime.tm_wday ) - { - /* a day following today */ - totalSeconds += (anotherDay - (brokenDownTime.tm_wday + 1)) * SECONDS_PER_DAY; - } - else - { - /* a day before today, handle the wrap around */ - d = (SATURDAY - brokenDownTime.tm_wday) + anotherDay; - - totalSeconds += d * SECONDS_PER_DAY; - - /* since the next day in the schedule is a day before today, schedule for next week or every other week */ - if ( pSchedElem->schedule.weeklySchedule == WEEKLY_CHOICE_EVERYOTHER_WEEK ) - { - totalSeconds += SECONDS_PER_DAY * 7; - } - } - - /* add in the time from midnight of the starting day to the start time */ - totalSeconds += pSchedElem->schedule.startTime.hour * SECONDS_PER_HOUR; - totalSeconds += pSchedElem->schedule.startTime.minute * SECONDS_PER_MINUTE; - - //printf("\t%s: seconds to next day in schedule(%ld)\n", __FUNCTION__, totalSeconds); - - calTime += totalSeconds; - localtime_r( &calTime, &newDate ); - - newDay = newDate.tm_mday; - newMonth = newDate.tm_mon + 1; - newYear = (newDate.tm_year - 100) + 2000; - - pSchedElem->schedule.startDate.year = newYear; - pSchedElem->schedule.startDate.month = newMonth; - pSchedElem->schedule.startDate.day = newDay; - - return; - } - - - /* - * The starting day is not today - * add in the time from now to end of day - * accumulate full days seconds from the day after today to starting day - */ - - hours = 24 - (brokenDownTime.tm_hour + 1); - minutes = 60 - (brokenDownTime.tm_min + 1); - seconds = 60 - brokenDownTime.tm_sec; - totalSeconds = SECONDS_PER_HOUR * hours; - totalSeconds += SECONDS_PER_MINUTE * minutes; - totalSeconds += seconds; - - /* count days from today until first day in schedule */ - if ( day > brokenDownTime.tm_wday ) - { - totalSeconds += (day - (brokenDownTime.tm_wday + 1)) * SECONDS_PER_DAY; - } - else - { - /* handle the wrap around */ - /* since the next day in the schedule is a day before today, schedule for next week or every other week */ - if ( pSchedElem->schedule.weeklySchedule == WEEKLY_CHOICE_EVERYOTHER_WEEK ) - { - totalSeconds += SECONDS_PER_DAY * 7; - } - d = ((6 - brokenDownTime.tm_wday) + day) % 7 ; - totalSeconds += d * SECONDS_PER_DAY; - } - - - /* add in the time from midnight of the starting day to the start time */ - totalSeconds += pSchedElem->schedule.startTime.hour * SECONDS_PER_HOUR; - totalSeconds += pSchedElem->schedule.startTime.minute * SECONDS_PER_MINUTE; - - calTime += totalSeconds; - localtime_r( &calTime, &newDate ); - - newDay = newDate.tm_mday; - newMonth = newDate.tm_mon + 1; - newYear = (newDate.tm_year - 100) + 2000; - - pSchedElem->schedule.startDate.year = newYear; - pSchedElem->schedule.startDate.month = newMonth; - pSchedElem->schedule.startDate.day = newDay; - - return; -} - -/* - * TSCutilScheduleOneWeekPerMonth - * - * The schedule is to start on a specific month week, i.e. First, Second, Third, Fourth, Last - * - * At least one day in the daily schedule must occur in the given week - * AND the daily schedule MUST NOT cross a month boundry. These constraints - * determine how the 'last' week of the month is interpreted. The 'last' week of the - * month must contain sufficient days to statisfy the daily schedule. - * Therefore 'last' may be the 4th or 5th weeks of the month. - * is the 4th week and in - * - * Given the current monthDay determine what monthWeek we are in and handle the following cases - * (1) In the current monthWeek - * Given the current weekDay is there a schedule-weekDay that works - * (both the day and time are in the future) if not the start is next month - * - * (2) Required monthWeek is yet to come - * compute time to first scheduled day in required monthWeek - * - * (3) Required monthWeek has passed - * compute time to required start next month - * - */ -void TSCutilScheduleOneWeekPerMonth( TSC_SCHED_ELEM *pSchedElem ) -{ - time_t calTime; - struct tm brokenDownTime, scheduledTime; - int currentYear, currentDay, currentMonth, dayOfWeek, dom, dow, wom, days, scheduleDay; - int currentMonthWeek, hours, minutes, seconds, totalSeconds; - int daysInSchedule, scheduleCheckCount; - CNTL_DATE_STRUCT currentDate, nextMonth; - - /* verify the schedule is requesting a week of the month */ - if ( pSchedElem->schedule.weeklySchedule != WEEKLY_CHOICE_FIRST_WEEK && - pSchedElem->schedule.weeklySchedule != WEEKLY_CHOICE_SECOND_WEEK && - pSchedElem->schedule.weeklySchedule != WEEKLY_CHOICE_THIRD_WEEK && - pSchedElem->schedule.weeklySchedule != WEEKLY_CHOICE_FOURTH_WEEK && - pSchedElem->schedule.weeklySchedule != WEEKLY_CHOICE_LAST_WEEK ) - { - printf("%s: Weekly schedule (%d) does not specify a week of the month\n", __FUNCTION__, pSchedElem->schedule.weeklySchedule); - return; - } - - brokenDownTime = pSchedElem->requestTime; - calTime = pSchedElem->calTime; - - currentYear = brokenDownTime.tm_year - 100; // tm_year: years since 1900 - currentYear += 2000; - currentMonth = brokenDownTime.tm_mon + 1; - currentDay = brokenDownTime.tm_mday; - - currentDate.year = currentYear; - currentDate.month = currentMonth; - currentDate.day = currentDay; - - currentMonthWeek = Util_getMonthWeek( pSchedElem, ¤tDate ); - - //printf(" %s: currentWeek(%d) scheduleWeek(%d)\n", __FUNCTION__, currentMonthWeek, pSchedElem->schedule.weeklySchedule); - - if ( currentMonthWeek < pSchedElem->schedule.weeklySchedule ) - { - /* the schedule week is in the future, move to the SUNDAY of the required week, find the first scheduled day and set startDate */ - dow = Util_getDayOfWeek( pSchedElem, ¤tDate ); - days = (SATURDAY - dow) + 1; /* days to start of next week */ - - /* count weeks up to the required week */ - for ( wom = currentMonthWeek + 1; wom < pSchedElem->schedule.weeklySchedule; ++wom ) - { - days += 7; - } - - /* find the first day (SUNDAY to SATURDAY) in the schedule */ - scheduleDay = Util_findAday( SUNDAY, pSchedElem->schedule.dailySchedule ); - - pSchedElem->schedule.startDate = currentDate; - pSchedElem->schedule.startDate.day += (days + scheduleDay); - - return; - } - - if ( currentMonthWeek == pSchedElem->schedule.weeklySchedule ) - { - /* - * the right week of the month, don't know the if the daily schedule fits, - * start with today and see if a day in the schedule fits - */ - dow = brokenDownTime.tm_wday; - //dow = Util_getDayOfWeek( pSchedElem, ¤tDate ); - scheduleDay = Util_findAday( dow, pSchedElem->schedule.dailySchedule ); - daysInSchedule = Util_numDaysInSchedule( pSchedElem->schedule.dailySchedule ); - - scheduleCheckCount = 0; - while ( scheduleCheckCount < daysInSchedule ) - { - //printf(" dow(%d) scheduleDay(%d)\n", dow, scheduleDay); - /* if dow is < schedule day OR (dow == schedule day AND the start time >= current time) the schedule fits */ - if ( (dow < scheduleDay) || /* first schedule day is after today */ - ( dow == scheduleDay && ( pSchedElem->schedule.startTime.hour > brokenDownTime.tm_hour || /* today is first day and the time hasn't passed */ - (pSchedElem->schedule.startTime.hour == brokenDownTime.tm_hour && pSchedElem->schedule.startTime.minute >= brokenDownTime.tm_min)) ) - ) - { - /* the schedule fits, update the starting date */ - pSchedElem->schedule.startDate = currentDate; - pSchedElem->schedule.startDate.day += (scheduleDay - dow); - - return; - } - ++scheduleCheckCount; - ++scheduleDay; - if ( scheduleDay <= SATURDAY ) - { - scheduleDay = Util_findAday( scheduleDay, pSchedElem->schedule.dailySchedule ); - } - - } - } - - /* - * required week has passed so schedule for next month - * - * By defintion the first day of the month defines the 'first' week of the month. - * count from the first week to the required week and update the start date - */ - //printf("%s: currentWeek > scheduleWeek\n", __FUNCTION__); - - nextMonth = currentDate; - if ( nextMonth.month == 12 ) - { - nextMonth.year += 1; - nextMonth.month = 1; - } - else - { - nextMonth.month += 1; - } - nextMonth.day = 1; - - /* get the day of week for the first day of nextMonth */ - dow = Util_getDayOfWeek( pSchedElem, &nextMonth ); - - if ( pSchedElem->schedule.weeklySchedule == WEEKLY_CHOICE_FIRST_WEEK ) - { - /* determine if the schedule fits in the very first week of the month */ - scheduleDay = Util_findAday( dow, pSchedElem->schedule.dailySchedule ); - - //printf("%s: dow(%s), firstDay(%s)\n", __FUNCTION__, Util_getDayString(dow), Util_getDayString(firstDay)); - - if ( dow <= scheduleDay ) - { - //printf("%s: schedule fits\n", __FUNCTION__); - /* the first day (and day of the week) of the month preceeds the first day in the schedule, set start date */ - pSchedElem->schedule.startDate = nextMonth; - pSchedElem->schedule.startDate.day += scheduleDay - dow; - - return; - } - - /* schedule doesn't fit starting from very first week so move to the next (and full) week */ - //printf("%s: moving schedule one week ahead\n",__FUNCTION__); - days = (SATURDAY - dow) + 1; - scheduleDay = Util_findAday( SUNDAY, pSchedElem->schedule.dailySchedule ); - pSchedElem->schedule.startDate = nextMonth; - pSchedElem->schedule.startDate.day += ( days + scheduleDay ); - - return; - } - - /* - * accumulate days up to SUNDAY of the required week - * add in the first scheduled day - * bobs your uncle - */ - //printf("%s: dow(%s)\n", __FUNCTION__, Util_getDayString(dow)); - - days = (SATURDAY - dow) + 1; - - /* count weeks up to the required week */ - for ( wom = 2; wom < pSchedElem->schedule.weeklySchedule; ++wom ) - { - days += 7; - } - - /* find the first day (SUNDAY to SATURDAY) in the schedule */ - scheduleDay = Util_findAday( SUNDAY, pSchedElem->schedule.dailySchedule ); - - //printf("%s: firstDay(%s)\n", __FUNCTION__, Util_getDayString(firstDay)); - - pSchedElem->schedule.startDate = nextMonth; - pSchedElem->schedule.startDate.day += (days + scheduleDay); - - return; -} - -/* - * Util_getDayOfWeek - * - * Get the day of week for a given day in the FUTURE - * This function asserts the given date is >= the current date - */ -int Util_getDayOfWeek( TSC_SCHED_ELEM *pSchedElem, CNTL_DATE_STRUCT *pDate ) -{ - time_t calTime; - struct tm brokenDownTime; - int currentYear, currentDay, currentMonth, dayOfWeek, days, d, dayDiff; - - calTime = pSchedElem->calTime; - brokenDownTime = pSchedElem->requestTime; - - currentYear = brokenDownTime.tm_year - 100; // tm_year: years since 1900 - currentYear += 2000; - currentMonth = brokenDownTime.tm_mon + 1; - currentDay = brokenDownTime.tm_mday; - - /* verify the given date assertion */ - if ( currentYear > pDate->year || - ( currentYear == pDate->year && currentMonth > pDate->month) || - (currentYear == pDate->year && currentMonth == pDate->month && currentDay > pDate->day) - ) - { - printf("%s: Given date (%2.2ld/%2.2ld/%2.2ld) is in the past\n", __FUNCTION__, pDate->month, pDate->day, pDate->year); - - return 0; - } - - /* - * The assertion verified the given date is >= the current date - */ - if ( currentYear < pDate->year ) - { - /* accumulate days for the remainder of month, */ - //days = UTgetDaysInMonth(currentYear, brokenDownTime.tm_mon); replaced with following tm_mon is months since jan, should be THE month - days = UTgetDaysInMonth(currentYear, currentMonth); - days -= currentDay; - - /* accumulate days to end of year */ - for ( d = currentMonth + 1; d <= 12; ++d ) - { - days += UTgetDaysInMonth(currentYear, d); - } - - /* accumulate days in following year up to GIVEN MONTH */ - for ( d = 0 + 1; d < pDate->month; ++d ) - { - days += UTgetDaysInMonth(pDate->year, d); - } - - /* days contains the number of days to the first day of the required month */ - - /* accumulate days in GIVEN MONTH */ - //days += (pDate->day - 1) ; AFH 5/26 - days += pDate->day; - - dayOfWeek = (brokenDownTime.tm_wday + days) % 7; - - return dayOfWeek; - } - else - { - /* same year */ - if ( currentMonth < pDate->month ) - { - /* same year, different month, start by accumulating days to end of month */ - //days = UTgetDaysInMonth(currentYear, brokenDownTime.tm_mon); replaced with following tm_mon is months since jan, should be THE month - days = UTgetDaysInMonth(currentYear, currentMonth); - days -= currentDay; - - /* accumulate months of days to the start of the given month */ - - for ( d = currentMonth + 1; d < pDate->month; ++d ) - { - days += UTgetDaysInMonth(currentYear, d); - } - - /* days contains the number of days to the first day of the required month */ - - /* add in the days to GIVEN DAY */ - //days += (pDate->day - 1) ; AFH 5/26 - days += pDate->day; - - dayOfWeek = (brokenDownTime.tm_wday + days) % 7; - - return dayOfWeek; - } - else - { - /* same year, same month so GIVEN DAY must be >= currentDay */ - if ( pDate->day > currentDay ) - { - /* count modulo 7 (day 0 is SUNDAY from current day to GIVEN DAY */ - dayDiff = pDate->day - currentDay; - dayOfWeek = (brokenDownTime.tm_wday + dayDiff) % 7; - return dayOfWeek; - } - else - { - /* same year, month, day */ - return brokenDownTime.tm_wday; - } - } - } -} - - - -char *Util_getDayString( int dayOfWeek ) -{ - switch ( dayOfWeek ) - { - case SUNDAY: - return "SUNDAY"; - - case MONDAY: - return "MONDAY"; - - case TUESDAY: - return "TUESDAY"; - - case WEDNESDAY: - return "WEDNESDAY"; - - case THURSDAY: - return "THURSDAY"; - - case FRIDAY: - return "FRIDAY"; - - case SATURDAY: - return "SATURDAY"; - - default: - return "UnKnown"; - } -} -void Util_displayDailySchedule( CNTL_SCHED_STRUCT *pTimeStruct ) -{ - int d; - printf("Daily Schedule : %s\n", Util_getDailyScheduleString(pTimeStruct)); - -} - - -char *Util_getDailyScheduleString( CNTL_SCHED_STRUCT *pTimeStruct ) -{ - int d; - static DMSTRING128 dailyString; - memset(dailyString, 0x0, sizeof(dailyString)); - - if ( pTimeStruct->dailySchedule & DAY_SCHEDULE_SUNDAY_MASK ) - { - strncat(dailyString, "Sunday", sizeof(dailyString) - strlen(dailyString) - 1); - strncat(dailyString, " ", sizeof(dailyString) - strlen(dailyString) - 1); - } - if ( pTimeStruct->dailySchedule & DAY_SCHEDULE_MONDAY_MASK ) - { - strncat(dailyString, "Monday", sizeof(dailyString) - strlen(dailyString) - 1); - strncat(dailyString, " ", sizeof(dailyString) - strlen(dailyString) - 1); - } - if ( pTimeStruct->dailySchedule & DAY_SCHEDULE_TUESDAY_MASK ) - { - strncat(dailyString, "Tuesday", sizeof(dailyString) - strlen(dailyString) - 1); - strncat(dailyString, " ", sizeof(dailyString) - strlen(dailyString) - 1); - } - if ( pTimeStruct->dailySchedule & DAY_SCHEDULE_WEDNESDAY_MASK ) - { - strncat(dailyString, "Wednesday", sizeof(dailyString) - strlen(dailyString) - 1); - strncat(dailyString, " ", sizeof(dailyString) - strlen(dailyString) - 1); - } - if ( pTimeStruct->dailySchedule & DAY_SCHEDULE_THURSDAY_MASK ) - { - strncat(dailyString, "Thursday", sizeof(dailyString) - strlen(dailyString) - 1); - strncat(dailyString, " ", sizeof(dailyString) - strlen(dailyString) - 1); - } - if ( pTimeStruct->dailySchedule & DAY_SCHEDULE_FRIDAY_MASK ) - { - strncat(dailyString, "Friday", sizeof(dailyString) - strlen(dailyString) - 1); - strncat(dailyString, " ", sizeof(dailyString) - strlen(dailyString) - 1); - } - if ( pTimeStruct->dailySchedule & DAY_SCHEDULE_SATURDAY_MASK ) - { - strncat(dailyString, "Saturday", sizeof(dailyString) - strlen(dailyString) - 1); - strncat(dailyString, " ", sizeof(dailyString) - strlen(dailyString) - 1); - } - return dailyString; -} - -char *Util_getWeeklyScheduleString( CNTL_SCHED_STRUCT *pTimeStruct ) -{ - char *week; - static DMSTRING128 weeklyString; - memset(weeklyString, 0x0, sizeof(weeklyString)); - - switch ( pTimeStruct->weeklySchedule ) - { - case WEEKLY_CHOICE_FIRST_WEEK: - week = "First"; - snprintf(weeklyString, sizeof(weeklyString), "The %s week of the month", week); - break; - - case WEEKLY_CHOICE_SECOND_WEEK: - week = "Second"; - snprintf(weeklyString, sizeof(weeklyString), "The %s week of the month", week); - break; - - case WEEKLY_CHOICE_THIRD_WEEK: - week = "Third"; - snprintf(weeklyString, sizeof(weeklyString), "The %s week of the month", week); - break; - - case WEEKLY_CHOICE_FOURTH_WEEK: - week = "Fourth"; - snprintf(weeklyString, sizeof(weeklyString), "The %s week of the month", week); - break; - - case WEEKLY_CHOICE_LAST_WEEK: - week = "Last"; - snprintf(weeklyString, sizeof(weeklyString), "The %s week of the month", week); - break; - - case WEEKLY_CHOICE_EVERY_WEEK: - snprintf(weeklyString, sizeof(weeklyString), "Every week of the month"); - break; - - case WEEKLY_CHOICE_EVERYOTHER_WEEK: - snprintf(weeklyString, sizeof(weeklyString), "Every other week of the month"); - break; - - default: - snprintf(weeklyString, sizeof(weeklyString), "Unknown week of the month"); - break; - } - - return weeklyString; -} - - -void Util_displayWeeklySchedule( CNTL_SCHED_STRUCT *pTimeStruct ) -{ - char *week; - printf("Weekly Schedule : %s\n", Util_getWeeklyScheduleString( pTimeStruct )); -} - -/* - * Load the time schedules from RL_Timer_Controls and build a time schedule list - */ -int TSCutilLoadSchedules() -{ - - return OK; - -} - -// version 2 -/* - * week_time_stamps indexed by 'days past Sunday', Sunday(0), Sat(6) - * contains the localtime in seconds for each day at midnight (past the epoch), based upon the bootup local time - * Given the localtime seconds for each day at midnight the local seconds for the schedule begin and end can be computed - * to determine if the schedule was missed AND to determine which missed schedule was closest to boot up. - */ -static unsigned long week_time_stamps[ 7 ]; - -/* - * chkpt_local_sec epoch time when the controller went down - * boot_local_sec epoch time when the controller booted up - * pElem pointer to scheduling struct with the beginning and ending time (hh:mm), the weekly choices, daily choices - * pReason pointer to return missed reason - * pMisses pointer to return number of missed schedules - * pStartToEndDiff pointer to return time in seconds from schedule start to bootup - */ -bool TSCutilEveryWeekMissedV2( CNTL_SCHED_STRUCT *pElem, unsigned long chkpt_local_sec, unsigned long boot_local_sec, TSC_SCHED_MISS_REASON *pReason, int *pMisses, int *pStartToEndDiff ) -{ -#if 0 - struct tm chkptBDT, sysbootBDT, sched_beginBDT, sched_endBDT; /* broken down time structures */ - bool ans = false; - unsigned long boot_spm; - unsigned long s_b, s_e, starting_time_stamp; - unsigned char down_days, down_intersection; - int day, missed_schedules, start_delta, i, d, day_diff; - char *pInfoReason; - DMSTRING32 buf; - - - do - { - /* - * If the daily schedule intersects the interruption interval then check the actual times - * chkptBDT - check-point-broken-down-time - the last check point taken before the system went down - * sysbootBDT - system-boot-broken-down-time - the boot time snapshot taken by the control system task - * - * for schedules that were missed or interrupted determine the nearest schedule start to the boot time (when the systen came back up) - * i.e. the smallest start_delta - */ - //localtime_r((time_t *)&boot_local_sec, &sysbootBDT); - //localtime_r((time_t *)&chkpt_local_sec, &chkptBDT); - localtime((time_t *)&boot_local_sec, &sysbootBDT); - localtime((time_t *)&chkpt_local_sec, &chkptBDT); - - /* - * Compute the week_time_stamps - * Starting with today (the day the system initialized) subtract the seconds past midnight from the boot time seconds - */ - day = sysbootBDT.tm_wday; - boot_spm = sysbootBDT.tm_hour * SECONDS_PER_HOUR; - boot_spm += sysbootBDT.tm_min * SECONDS_PER_MINUTE; - boot_spm += sysbootBDT.tm_sec; - - starting_time_stamp = boot_local_sec - boot_spm; - - /* now fill in the remaining days of the week, the wrap around assumes all days are prior to the reboot */ - for ( i = 0, d = day; i < 7; ++i ) - { - day_diff = day - d ; - if ( day_diff < 0 ) - { - day_diff = 7 + day_diff; - } - week_time_stamps[ d ] = starting_time_stamp - (SECONDS_PER_DAY * day_diff); - //UTformatDateAndTimeGivenSeconds( week_time_stamps[d], buf, sizeof(buf) ); - //printf("\t%s\n", buf); - ++d; - d = d % 7; - - } - -#ifdef COMMENTOUT - printf("\n\nBootup on day(%d) week_time_stamps follow\n", day); - - for ( i = 0; i < 7; ++i ) - { - UTformatDateAndTimeGivenSeconds( week_time_stamps[i], buf, sizeof(buf) ); - printf("\t%s\n", buf); - } - printf("\n"); -#endif - missed_schedules = 0; - start_delta = SECONDS_PER_DAY * 365; - - *pReason = TSC_SCHEDULE_NOT_MISSED; - - /* - * create a bit field representing of the days the controller was down on a schedule feed day - * boot_local_sec - chkpt_local_sec - * if down for more than 7 days the mask is all one's - */ - if ( boot_local_sec - chkpt_local_sec >= SECONDS_PER_DAY * 7 ) - { - down_days = 0x7f; - } - else - { - down_days = Util_computeDownMask( chkptBDT.tm_wday, sysbootBDT.tm_wday ); - - } - - down_intersection = down_days & pElem->dailySchedule; - - if ( timer_debug_print ) - { - printf("\t%s: feed schedule & down interval intersection(0x%x)\n", __FUNCTION__,down_intersection); - } - - /* - * for each intersecting day determine if the feed schedule was interrupted or missed - */ - for ( day = 0; day < 7; ++day ) - { - if ( down_intersection & (0x01 << day) ) - { - /* compute the epoch seconds schedule beginning, midnight seconds + starting hh:mm:ss */ - s_b = week_time_stamps[day]; - s_b += pElem->startTime.hour * SECONDS_PER_HOUR; - s_b += pElem->startTime.minute * SECONDS_PER_MINUTE; - s_b += pElem->startTime.second; - - /* compute the epoch seconds schedule end, midnight seconds + starting hh:mm:ss */ - s_e = week_time_stamps[day]; - s_e += pElem->endTime.hour * SECONDS_PER_HOUR; - s_e += pElem->endTime.minute * SECONDS_PER_MINUTE; - s_e += pElem->endTime.second; - s_e -= 1; /* for scheduling purposes the end time is reduced by 1 second, so the end of timer-A is the same as the start of timer-B */ - - localtime_r((time_t *)&s_b, &sched_beginBDT); - -#ifdef COMMENTOUT - printf("Inspecting Schedule %2.2d/%2.2d %2.2ld:%2.2ld to %2.2ld:%2.2ld", - sched_beginBDT.tm_mon+1, sched_beginBDT.tm_mday, pElem->startTime.hour, pElem->startTime.minute, pElem->endTime.hour, pElem->endTime.minute); -#endif - /* - * determine if a schedule was interrupted or missed - * a schedule is interrupted if it begins before chkpt_local_sec and ends after chkpt_local_sec - * a schedule is missed if it is between chkpt_local_sec and boot_local_sec or - * it starts after down_begin and stops after down_end - */ - if ( (s_b < chkpt_local_sec && chkpt_local_sec < s_e ) || // straddle the checkpoint - (s_b > chkpt_local_sec && s_e < boot_local_sec) || // in between checkpoint & bootup - (s_b < boot_local_sec && s_e > boot_local_sec) // straddle bootup - ) - { - ++missed_schedules; - - start_delta = (boot_local_sec - s_b) < start_delta ? boot_local_sec - s_b : start_delta; - // between power down and power up - // straddle power up - if ( (chkpt_local_sec <= s_b && s_e <= boot_local_sec) || ( s_b <= boot_local_sec && s_e >= boot_local_sec ) ) - { - *pReason = TSC_SCHEDULE_MISSED; - pInfoReason = "missed"; - } - else if ( s_b <= chkpt_local_sec && chkpt_local_sec < s_e ) // straddle power down - { - *pReason = TSC_SCHEDULE_INTERRUPTED; - pInfoReason = "interrupted"; - - } - - printf("schedule %s %2.2d/%2.2d %2.2ld:%2.2ld to %2.2ld:%2.2ld %s, controller down from %2.2d/%2.2d %2.2d:%2.2d to %2.2d/%2.2d %2.2d:%2.2d", - Util_getDayString(day), sched_beginBDT.tm_mon+1, sched_beginBDT.tm_mday, pElem->startTime.hour, pElem->startTime.minute, pElem->endTime.hour, pElem->endTime.minute, pInfoReason, - chkptBDT.tm_mon+1, chkptBDT.tm_mday, chkptBDT.tm_hour, chkptBDT.tm_min, sysbootBDT.tm_mon+1, sysbootBDT.tm_mday, sysbootBDT.tm_hour, sysbootBDT.tm_min ); - } - else - { - printf("Schedule %2.2d/%2.2d %2.2ld:%2.2ld to %2.2ld:%2.2ld NOT Missed", - sched_beginBDT.tm_mon+1, sched_beginBDT.tm_mday, pElem->startTime.hour, pElem->startTime.minute, pElem->endTime.hour, pElem->endTime.minute); - } - } - } - } while ( 0 ); - - ans = ( missed_schedules ) ? true : false; - - *pMisses = missed_schedules; - *pStartToEndDiff = start_delta; - - return ans; -#else - return false; -#endif -} - - - -// version 1 -bool TSCutilEveryWeekMissed( CNTL_SCHED_STRUCT *pElem, unsigned long chkpt_local_sec, unsigned long chkpt_spm, int chkpt_wday, unsigned long boot_local_sec, unsigned long boot_spm, int boot_wday, unsigned long sched_start, unsigned long sched_end, TSC_SCHED_MISS_REASON *pReason, int *pMisses, int *pStartToEndDiff ) -{ - bool ans = false; - unsigned long down_begin, down_end; - unsigned long b_h, b_m, e_h, e_m; - unsigned long s_b_h, s_b_m, s_e_h, s_e_m, s_b, s_e; - unsigned char down_days, down_intersection; - int day, missed_schedules, start_delta; - - do - { - /* - * If the daily schedule intersects the interruption interval then check the actual times - * chkptBDT - check-point-broken-down-time - the last check point taken before the system went down - * sysbootBDT - system-boot-broken-down-time - the boot time snapshot taken by the control system task - * - * for schedules that were missed or interrupted determine the nearest schedule start to the boot time (when the systen came back up) - * i.e. the smallest start_delta - */ - - /* - * Compute the week_time_stamps - * Starting with today (the day the system initialized) subtract the seconds past midnight from the boot time seconds - */ - day = boot_wday; - - missed_schedules = 0; - start_delta = SECONDS_PER_DAY * 365; - - if ( timer_debug_print ) - { - printf("\t%s: daily schedule(0x%x), checkPoint day(%1.1d) boot day(%1.1d)\n", __FUNCTION__,pElem->dailySchedule, chkpt_wday, boot_wday); - } - - *pReason = TSC_SCHEDULE_NOT_MISSED; - /* - * create a bit field representing of the days the controller was down on a schedule feed day - */ - down_days = Util_computeDownMask( chkpt_wday, boot_wday ); - down_intersection = down_days & pElem->dailySchedule; - - if ( timer_debug_print ) - { - printf("\t%s: feed schedule & down interval intersection(0x%x)\n", __FUNCTION__,down_intersection); - } - - /* - * for each intersecting day determine if the feed schedule was interrupted or missed - */ - for ( day = 0; day < 7; ++day ) - { - if ( down_intersection & (0x01 << day) ) - { - /* an entire day of 'down' is identified as down_begin == 0 and down_end == SECONDS_PER_DAY */ - Util_computeBeginEndDownForDay( chkpt_wday, chkpt_spm, boot_wday, boot_spm, day, &down_begin, &down_end); - - /* - * determine if a schedule was interrupted or missed - * a schedule is interrupted if it starts before down_begin and stops after down_begin - * a schedule is missed if it is between down_begin and down_end or - * it starts after down_begin and stops after down_end - */ - if ( (down_begin <= sched_start && sched_end <= down_end) || - ( sched_start <= down_end && sched_end >= down_end ) || - ( sched_start <= down_begin && down_begin < sched_end ) ) - { - ++missed_schedules; - - start_delta = (down_end - sched_start < start_delta) ? down_end - sched_start : start_delta; - - /* the schedule was missed or was interrupted, compute the schedule starting and ending time for the log message */ - s_b_h = sched_start / SECONDS_PER_HOUR; - s_b_m = (sched_start - SECONDS_PER_HOUR * s_b_h)/SECONDS_PER_MINUTE; - - /* 1 second is subtracted for scheduling purpoes so add it back so a 1 minute timer appears to start and stop on different minutes */ - s_e_h = sched_end / SECONDS_PER_HOUR; - s_e_m = (1 + (sched_end - SECONDS_PER_HOUR * s_e_h))/SECONDS_PER_MINUTE; - if ( s_e_m == MINUTES_PER_HOUR ) - { - s_e_h += 1; - s_e_m = 0; - } - - b_h = down_begin/SECONDS_PER_HOUR; - b_m = (down_begin - SECONDS_PER_HOUR * b_h)/SECONDS_PER_MINUTE; - - e_h = down_end/SECONDS_PER_HOUR; - e_m = (down_end - SECONDS_PER_HOUR * e_h)/SECONDS_PER_MINUTE; - - if (down_begin <= sched_start && sched_end <= down_end) - { - *pReason = TSC_SCHEDULE_MISSED; - printf("%s schedule %2.2ld:%2.2ld to %2.2ld:%2.2ld missed, controller down from %2.2ld:%2.2ld to %2.2ld:%2.2ld", - Util_getDayString(day), s_b_h, s_b_m, s_e_h, s_e_m, b_h, b_m, e_h, e_m ); - } - else if ( sched_start <= down_end && sched_end >= down_end ) - { - *pReason = TSC_SCHEDULE_MISSED; - printf("%s schedule %2.2ld:%2.2ld to %2.2ld:%2.2ld missed, controller down from %2.2ld:%2.2ld to %2.2ld:%2.2ld", - Util_getDayString(day), s_b_h, s_b_m, s_e_h, s_e_m, b_h, b_m, e_h, e_m ); - - } - else if ( sched_start <= down_begin && down_begin < sched_end ) - { - printf("%s schedule %2.2ld:%2.2ld to %2.2ld:%2.2ld interrupted, controller down from %2.2ld:%2.2ld to %2.2ld:%2.2ld", - Util_getDayString(day), s_b_h, s_b_m, s_e_h, s_e_m, b_h, b_m, e_h, e_m ); - *pReason = TSC_SCHEDULE_INTERRUPTED; - } - } - } - } - - } while ( 0 ); - - ans = ( missed_schedules ) ? true : false; - - *pMisses = missed_schedules; - *pStartToEndDiff = start_delta; - - return ans; -} - - -unsigned char Util_computeDownMask( int begin_wday, int end_wday ) -{ - char startStopMap; - int i, b_day, e_day, num_days; - - startStopMap = 0; - - if ( timer_debug_print ) - { - printf("\t%s: begin_wday(%d), end_wday(%d)\n", __FUNCTION__,begin_wday, end_wday); - } - - /* adding one to change from days past sunday to day of the week */ - b_day = begin_wday +1 ; - e_day = end_wday + 1; - - /* - * start_day > stop_day the weekend was crossed - */ - if ( b_day > e_day ) - { - num_days = 7 - b_day; - num_days += e_day; - } - else - { - num_days = e_day - b_day; - } - ++num_days; - - if ( timer_debug_print ) - { - printf("\t%s: b_day(%d), e_day(%d), num_days(%d)\n", __FUNCTION__, b_day, e_day, num_days); - } - - b_day = begin_wday; - - if ( timer_debug_print ) - { - printf("\t%s: b_day(%d)\n", __FUNCTION__, b_day); - } - - for ( i = 0; i < num_days; ++i ) - { - startStopMap = startStopMap | (0x01 << b_day); - ++b_day; - b_day = b_day % 7; - } - - if ( timer_debug_print ) - { - printf("\t%s: startStopMap(0x%x)\n", __FUNCTION__, startStopMap); - } - - return startStopMap; -} - -/* - * Util_computeBeginEndDownForDay for the given day compute the down time begin and end - * The 'day' parameter is a day in the down time interval, it can be the first day of the down interval or a day within the interval - * Consider the follwing down time intervals - * the chkpt and boot day are the same - * the chkpt and boot day span 2 or more days - * - * down_begin and down_end are speconds past midnight - */ -void Util_computeBeginEndDownForDay( int chkpt_wday, unsigned long chkpt_spm, int boot_wday, unsigned long boot_spm, int day, unsigned long *down_begin, unsigned long *down_end) -{ - if ( day == chkpt_wday ) - { - *down_begin = chkpt_spm; - - if ( day == boot_wday) - { - *down_end = boot_spm; - } - else - { - *down_end = SECONDS_PER_DAY; - } - } - else - { - *down_begin = 0; - - if ( day == boot_wday) - { - *down_end = boot_spm; - } - else - { - *down_end = SECONDS_PER_DAY; - } - } - - if ( timer_debug_print ) - { - printf("\t%s: chkpt_wday(%d), chkpt_spm(%ld), boot_wday(%d), boot_spm(%ld) day(%d), down_being(%ld), down_end(%ld)\n", - __FUNCTION__,chkpt_wday, chkpt_spm, boot_wday, boot_spm, day, *down_begin, *down_end); - } -} - -int TSCgetNumericSuffix(char* input) -{ - if (!input) { - return -1; - } - int instance = 0; - int multiplier = 1; - char* p = input + strlen(input); - - while (--p >= input) { - if (*p < '0' || *p > '9') { - break; - } - instance += multiplier * (*p - '0'); - multiplier *= 10; - } - return instance; -} - - -/* - * TSCnewComputeWaitTimer - * - * - */ -int TSCnewComputeWaitTimer( CNTL_SCHED_STRUCT *pSchedule, TSC_UTIL_SCHED_STATE scheduleingState, TSC_SCHED_ELEM *pNowTimeDate, unsigned long *pDiffInSeconds ) -{ -#if 0 - TSC_SCHED_ELEM schedTimeDate; - int chk; - - /* - * Schedule the next timer activity - * - * The timer is either - * Regular priority - no special treatment - * High priority and was not interrupted - * High priority was interrupted but a restart is not allowed, in which case the missed timer alarm was set - * - * - * compute the time difference from now until the start of the schedule - * schedTimeDate starts with current date and TSCutilComputeStartDate will - * move the starting date to the next date in the schedule based upon the - * starting time and the daily + weekly schedule - */ - pNowTimeDate->calTime = time( NULL ); - - localtime_r( &pNowTimeDate->calTime, &pNowTimeDate->requestTime ); - - pNowTimeDate->schedule.startDate.year = pNowTimeDate->requestTime.tm_year - 100; /* years since 1900 */ - pNowTimeDate->schedule.startDate.year += 2000; - pNowTimeDate->schedule.startDate.month = pNowTimeDate->requestTime.tm_mon + 1; /* months 1 - 12 */ - pNowTimeDate->schedule.startDate.day = pNowTimeDate->requestTime.tm_mday; - pNowTimeDate->schedule.startTime.hour = pNowTimeDate->requestTime.tm_hour; - pNowTimeDate->schedule.startTime.minute = pNowTimeDate->requestTime.tm_min; - pNowTimeDate->schedule.startTime.second = pNowTimeDate->requestTime.tm_sec; - - /* setup schedTimeDate with the current date, time and the required schedule */ - schedTimeDate.schedule.startDate = pNowTimeDate->schedule.startDate; - schedTimeDate.schedule.startTime = pSchedule->startTime; - schedTimeDate.schedule.dailySchedule = pSchedule->dailySchedule; - schedTimeDate.schedule.weeklySchedule = pSchedule->weeklySchedule; - schedTimeDate.calTime = pNowTimeDate->calTime; - schedTimeDate.requestTime = pNowTimeDate->requestTime; - - /* - * Adjust the starting date to correspond with the first scheduled event - * This is where the heavy lifting occurs - * TSCutilComputeStartDate will move the date from 'now' to the next date that satisfies the schedule - * Util_dateTimeDiff computes the difference in seconds of schedTimeDate and nowTimeDate - */ - //printf("\t%s: startTime.hour(%ld), startTime.minute(%ld)\n", __FUNCTION__, schedTimeDate.schedule.startTime.hour, schedTimeDate.schedule.startTime.minute); - TSCutilComputeStartDate( &schedTimeDate ); - -#ifdef COMMENTOUT - printf(" schedTimeDateDate(%2.2ld/%2.2ld/%4.4ld),schedTimeDateTime(%2.2ld:%2.2ld)\n\n", - schedTimeDate.schedule.startDate.month, schedTimeDate.schedule.startDate.day, schedTimeDate.schedule.startDate.year, - schedTimeDate.schedule.startTime.hour, schedTimeDate.schedule.startTime.minute); -#endif - - /* - * Util_dateTimeDiff computes the difference (in seconds) from now (date and time) to - * schedTimeDate (date and scheduled starting hour, minute, second) - */ - if ( Util_dateTimeDiff( &pNowTimeDate->schedule, &schedTimeDate.schedule, pDiffInSeconds ) != OK ) - { - printf("%s: Util_dateTimeDiff failed", __FUNCTION__ ); - return ERROR; - } - - /* - * The computed start date should NOT be today if this is a reschedule - * a given timer schedule may execute only ONCE per day - */ - if ( scheduleingState == TSC_RESCHEDULE ) - { - /* - * the scheduled start date should not be the current date so move the - * 'current' reference time forward to 1 minute past the schedule start time - * and recompute the starting date and time prior to computing the difference interval - */ - if ( memcmp(&pNowTimeDate->schedule.startDate, &schedTimeDate.schedule.startDate, sizeof(pNowTimeDate->schedule.startDate)) == 0 ) - { - printf("\tTime change fall back case: start (%ld/%ld/%ld),(%ld:%ld)\n", - schedTimeDate.schedule.startDate.month, schedTimeDate.schedule.startDate.day, schedTimeDate.schedule.startDate.year, - schedTimeDate.schedule.startTime.hour, schedTimeDate.schedule.startTime.minute); - printf("\tTime change fall back case: diffInSeconds(%ld)\n", *pDiffInSeconds); - - /* - * diffInSeconds is the interval inseconds to the go-off - * Add one minute to the diff and reschedule the timer from that time - * which will result in a date and time past today - */ - *pDiffInSeconds += SECONDS_PER_MINUTE; - - printf("\tadding (%ld) seconds and recomputing\n", *pDiffInSeconds); - - /* - * establish the new fall-back time reference - * move the scheduleTimeDate time to 1 minute past the scheduled go-off - * reschedule with a request time that is past the scheduled start - */ - schedTimeDate.calTime += *pDiffInSeconds; - localtime_r( &schedTimeDate.calTime, &schedTimeDate.requestTime ); - - TSCutilComputeStartDate( &schedTimeDate ); - - /* this diff is from now time to the rescheduled start time */ - if ( Util_dateTimeDiff( &pNowTimeDate->schedule, &schedTimeDate.schedule, pDiffInSeconds ) != OK ) - { - printf("Util_dateTimeDiff failed" ); - return ERROR; - } - - /* The new diff based upon now and the re-re-scheduled time */ - printf("\trecomputed diffInSeconds(%ld)\n", *pDiffInSeconds); - } - } -#endif - return OK; -} - - -typedef struct tsc_down_time_struct -{ - CNTL_SCHED_STRUCT sched_elem; // timer schedule - unsigned long chkpt_sec; // checkpoint time before controller went down - unsigned long chkpt_spm; // checkpoint seconds past midnight - int chkpt_wday; // checkpoint weekday - unsigned long boot_sec; // time when controller reinitialized - unsigned long boot_spm; // bootup seconds past midnight - int boot_wday; // bootup weekday - unsigned long sched_start;// schedule start seconds past midnight - unsigned long sched_end; // schedule end seconds past midnight - int *pMisses; // return the number of missed schedules - int *pStartToEndDiff; // return the time interval from schedule to bootup in seconds - TSC_SCHED_MISS_REASON *pReason; // return the reason for schedule miss -} TSC_DOWN_TIME_STRUCT; - - -/* - * TSCcountMissedSchedules( someStruct *pstruct ) - * - * The function TSCcountMissedSchedules will replace the function TSCutilEveryWeekMissed - * - * count the number of timer missed timer schedules between the checkpoint and boot-up times. - * - * for each day between the check point day (the day the controller went down) and boot up day - * 1. compute the beginning and ending down time in terms of seconds past midnight for the day - * 2. determine if the timer had a schedule that day - * 3. determine if the schedule was interrupted or completely missed - * 4. accumulate missed or interrupted schedules - * 5. determine the closest missed schedule to the boot up time - * - * - */ - -bool TSCcountMissedSchedules( TSC_DOWN_TIME_STRUCT *pDTinfo) -{ - return false; -} - -#ifdef __ICCARM__ -struct tm *localtime_r(const time_t *const timep, struct tm *result) -{ - struct tm *time; - localtimeMutex.lock(); - time = localtime(timep); - if ( time ) { - *result = *time; - } - localtimeMutex.unlock(); - return time ? result : NULL; -} -#endif - -
--- a/ICE-Application/src/Utilities/timerUtils.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,269 +0,0 @@ -#ifndef __CNTL_TIME_SHEDULE_UTILS_H__ -#define __CNTL_TIME_SHEDULE_UTILS_H__ -/************************************************************************ - * * - * Copyright (c) 2012 Nalco Company. All Rights Reserved. * - * * - ************************************************************************/ - -#include <mbed.h> -#include "rtos.h" - -#ifdef __ICCARM__ -struct tm *localtime_r(const time_t *const timep, struct tm *result); -#endif - -typedef char DMSTRING32[32]; -typedef char DMSTRING128[128]; -typedef DMSTRING32 DM_TAG; - -#define ERROR -1 -#define OK 0 - -typedef enum cntl_check_schedule_returns -{ - CNTL_SCHED_CHECK_INTERNAL_ERROR = -3, // internal implementation error - CNTL_SCHED_CHECK_BAD_PARAMS = -2, // problem with the schedule data passed - CNTL_SCHED_CHECK_SCHED_OK = 0, // no problems or conflicts with the schedule - CNTL_SCHED_CHECK_OUTPUT_CONFLICT = 1, // conflict on output relay - CNTL_SCHED_CHECK_PRODUCT_CONFLICT = 2, // product conflict with another relay timer - CNTL_SCHED_CHECK_BLOWDOWN_CONFLICT = 3 // blowdown conflict with another relay timer -} CNTL_CHECK_SCHEDULE_RETURNS; - -#define CNTL_MAX_SCHEDULE_ELEMENTS 64 -#define TSC_ALARM_PRIORITY (90) /* Very high alarm priority so no other alarm can cancel it, i.e. interlock, maintenance mode, etc.*/ -#define TSC_12_HOURS_IN_SECONDS (SECONDS_PER_MINUTE * MINUTES_PER_HOUR * 12 ) -#define TSC_31_DAYS_IN_SECONDS (SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * 31) - - -#define SUNDAY_MASK (0x001) -#define MONDAY_MASK (0x002) -#define TUESDAY_MASK (0x004) -#define WEDNESDAY_MASK (0x008) -#define THURSDAY_MASK (0x010) -#define FRIDAY_MASK (0x020) -#define SATURDAY_MASK (0x040) -#define EVERY_DAY_MASK (0x080) -#define EVERY_WEEK_DAY_MASK (0x100) -#define SUN_TO_SAT_MASK (SUNDAY_MASK | MONDAY_MASK | TUESDAY_MASK | WEDNESDAY_MASK | THURSDAY_MASK | FRIDAY_MASK | SATURDAY_MASK) - -typedef struct -{ - int days; - int hours; - int minutes; - int seconds; -} UTtimeInterval; - -/* - * A general date and time struct - */ -typedef struct -{ - int hour; - int minute; - int day; - int month; - int year; -} UT_DATE_TIME; - -typedef struct cntl_date_struct -{ - unsigned long month; - unsigned long day; - unsigned long year; -} CNTL_DATE_STRUCT; - -typedef struct cntl_hr_min_struct -{ - unsigned long hour; - unsigned long minute; - unsigned long second; -} CNTL_HR_MIN_STRUCT; - -#define SECONDS_PER_MINUTE (60) -#define MINUTES_PER_HOUR (60) -#define HOURS_PER_DAY (24) -#define DAYS_PER_WEEK (7) -#define DAYS_PER_YEAR (365) -#define HOURS_PER_WEEK (HOURS_PER_DAY * DAYS_PER_WEEK) -#define MINUTES_PER_DAY (MINUTES_PER_HOUR * HOURS_PER_DAY) -#define MINUTES_PER_WEEK (MINUTES_PER_HOUR * HOURS_PER_WEEK) -#define SECONDS_PER_HOUR (SECONDS_PER_MINUTE*MINUTES_PER_HOUR) -#define SECONDS_PER_DAY (SECONDS_PER_HOUR*HOURS_PER_DAY) -#define SECONDS_PER_WEEK (SECONDS_PER_HOUR*HOURS_PER_WEEK) - - -#define CNTL_SUNDAY_MASK (0x01) -#define CNTL_MONDAY_MASK (0x02) -#define CNTL_TUESDAY_MASK (0x04) -#define CNTL_WEDNESDAY_MASK (0x08) -#define CNTL_THURSDAY_MASK (0x10) -#define CNTL_FRIDAY_MASK (0x20) -#define CNTL_SATURDAY_MASK (0x40) -#define CNTL_SUN_TO_SAT_MASK (CNTL_SUNDAY_MASK | CNTL_MONDAY_MASK | CNTL_TUESDAY_MASK | CNTL_WEDNESDAY_MASK | CNTL_THURSDAY_MASK | CNTL_FRIDAY_MASK | CNTL_SATURDAY_MASK) -#define CNTL_WEEKDAY_MASK (CNTL_MONDAY_MASK | CNTL_TUESDAY_MASK | CNTL_WEDNESDAY_MASK | CNTL_THURSDAY_MASK | CNTL_FRIDAY_MASK) - -typedef enum -{ - DAY_SCHEDULE_NOT_SPECIFIED = 0x00, - DAY_SCHEDULE_SUNDAY_MASK = 0x01, - DAY_SCHEDULE_MONDAY_MASK = 0x02, - DAY_SCHEDULE_TUESDAY_MASK = 0x04, - DAY_SCHEDULE_WEDNESDAY_MASK = 0x08, - DAY_SCHEDULE_THURSDAY_MASK = 0x10, - DAY_SCHEDULE_FRIDAY_MASK = 0x20, - DAY_SCHEDULE_SATURDAY_MASK = 0x40, - DAY_SCHEDULE_WEEKDAY_MASK = ( DAY_SCHEDULE_MONDAY_MASK | DAY_SCHEDULE_TUESDAY_MASK | DAY_SCHEDULE_WEDNESDAY_MASK | DAY_SCHEDULE_THURSDAY_MASK | DAY_SCHEDULE_FRIDAY_MASK ), - DAY_SCHEDULE_SAT_TO_SUN_MASK = ( DAY_SCHEDULE_SUNDAY_MASK | DAY_SCHEDULE_WEEKDAY_MASK | DAY_SCHEDULE_SATURDAY_MASK ) -} DAY_SCHEDULE; - -typedef enum -{ - WEEKLY_CHOICE_EVERY_WEEK = 0, - WEEKLY_CHOICE_FIRST_WEEK = 1, - WEEKLY_CHOICE_SECOND_WEEK = 2, - WEEKLY_CHOICE_THIRD_WEEK = 3, - WEEKLY_CHOICE_FOURTH_WEEK = 4, - WEEKLY_CHOICE_LAST_WEEK = 5, - WEEKLY_CHOICE_EVERYOTHER_WEEK = 6, - WEEKLY_CHOICE_NOT_SPECIFIED = 7 -} WEEKLY_CHOICES; - - -typedef enum cntl_days_of_wk -{ - SUNDAY = 0, - MONDAY = 1, - TUESDAY = 2, - WEDNESDAY = 3, - THURSDAY = 4, - FRIDAY = 5, - SATURDAY = 6 -} CNTL_DAYS_OF_WK; - -/* - * CNLT_SCHED_STRUCT contains everything needed to specify a schedule - */ -typedef struct cntl_sched_struct -{ - CNTL_DATE_STRUCT startDate; // always valid, mm/dd/yyyy when schedule starts all schedules have a startDate - CNTL_HR_MIN_STRUCT startTime; // always valid, hour and minute when output is activated, all schedules have startTime - CNTL_HR_MIN_STRUCT endTime; // always valid, hour and minute when output is activation stops, all schedules have stopTime - WEEKLY_CHOICES weeklySchedule; // default is EVERY - unsigned char dailySchedule; // default is the current day of the week -} CNTL_SCHED_STRUCT; - -typedef struct -{ - CNTL_SCHED_STRUCT schedule; /* the schedule */ - time_t calTime; /* time in seconds passed to localtime to populate the requestTime structure */ - struct tm requestTime; /* the broken down time when the request was made, used to determine the initial starting date */ - unsigned long outputDuty; /* scaled output duty 3 digits 1 decimal 0 <= outputDuty <= 1000 */ - bool preBleedorLockout; /* the timer specifies preBleed or lockout */ -} TSC_SCHED_ELEM; - -#define TSC_NULL_INSTANCE (-1) - -/* - * To be deprecated - */ -typedef enum -{ - TSC_UTIL_ONE_OUTPUT = 1, - TSC_UTIL_ALL_OUTPUTS = 2, -} TSC_UTIL_OUTPUT_CHECK; - -/* - * Tell the general checking function what to look for - */ -typedef enum -{ - TSC_UTIL_THEOUTPUT_CHECK, - TSC_UTIL_BLOWDOWN_CHECK, - TSC_UTIL_OXNONOX_CHECK -} TSC_UTIL_WHAT_TO_CHECK; - -/* - * The attribute to look for when filtering the list of timer schedules - * it's up to the pruning filter functions to include or exclude based upon the - * pruning attribute - */ -typedef enum -{ - TSC_UTIL_PRUNE_PRODUCTS = 1, - TSC_UTIL_PRUNE_BLOWDOWN = 2 -} TSC_UTIL_PRUNE_ATTRIBUTE; - -/* - * attribute indicates if a timer scheduling is for the first time or a reschedule - */ -typedef enum -{ - TSC_INITIAL_SCHEDULE, - TSC_RESCHEDULE -} TSC_UTIL_SCHED_STATE; - -typedef enum tsc_sched_miss_reason -{ - TSC_SCHEDULE_NOT_MISSED, - TSC_SCHEDULE_MISSED, - TSC_SCHEDULE_INTERRUPTED -} TSC_SCHED_MISS_REASON; - - -/* - * renamed utility functions replacing CNTL_xyz functions - */ - -void TSCutilScheduleWithInWeek( TSC_SCHED_ELEM *pSchedElem ); -void TSCutilPreviousScheduleWithInWeek( TSC_SCHED_ELEM *pSchedElem ); -void TSCutilComputeStartDate( TSC_SCHED_ELEM *pSchedElem ); -void TSCutilScheduleOneWeekPerMonth( TSC_SCHED_ELEM *pSchedElem ); -void TSCutilInitFreeLists(); -bool TSCutilWasPreviousScheduleInterrupted( CNTL_SCHED_STRUCT *pElem, TSC_SCHED_MISS_REASON *pReason, unsigned long create_time_stamp, int *pmiss_count, int *pstart_to_end_diff ); -int TSCgetCreateTime( DM_TAG relay, unsigned long starting_hour, unsigned long starting_minute, unsigned long *create_time ); -int TSCnewComputeWaitTimer( CNTL_SCHED_STRUCT *pSchedule, TSC_UTIL_SCHED_STATE scheduleingState, TSC_SCHED_ELEM *pNowTimeDate, unsigned long *pDiffInSeconds ); -bool TSCIsTimerSchedLT12Hr( unsigned long *pGo_Off, DMSTRING32 id_info ); - -int TSCutilLoadSchedules(); -int TSCutilAddElemToScheduleList( TSC_SCHED_ELEM *pElem ); -int TSCutilPutFreeSchedElem( TSC_SCHED_ELEM *pElem ); - - -int TSCutilDeleteSchedElem( TSC_SCHED_ELEM *pElem ); - -TSC_SCHED_ELEM *TSCutilGetFreeSchedElem( ); -TSC_SCHED_ELEM *TSCutilGetFirstSchedule(); -TSC_SCHED_ELEM *TSCutilGetNextSchedule( TSC_SCHED_ELEM *pElem ); - -int TSCutilgetScheduleCount(); -int TSCutilCmpStartTimes( TSC_SCHED_ELEM *pNewElem, TSC_SCHED_ELEM *pListElem ); - -bool TSCutilDoesScheduleConflict( TSC_SCHED_ELEM *pNewElem, TSC_UTIL_OUTPUT_CHECK which_check ); - - - -int Util_addTupleToTimeControl( int instance, TSC_SCHED_ELEM *pElem ); -int Util_addTupleToRLcontrol( int instance, TSC_SCHED_ELEM *pElem, DM_TAG keyTag ); -int Util_dateTimeDiff( CNTL_SCHED_STRUCT *pDateTime1, CNTL_SCHED_STRUCT *pDateTime2, unsigned long *pDiffInSeconds ); - - -void Util_displayStartDateTime( CNTL_SCHED_STRUCT *pTimeStruct ); -void Util_displayWeeklySchedule( CNTL_SCHED_STRUCT *pTimeStruct ); -void Util_displayDailySchedule( CNTL_SCHED_STRUCT *pTimeStruct ); - -int Util_getDayOfWeek( TSC_SCHED_ELEM *pSchedElem, CNTL_DATE_STRUCT *pDate ); -int Util_getMonthWeek( TSC_SCHED_ELEM *pSchedElem, CNTL_DATE_STRUCT *pDate ); -int Util_findAday( int startingDay, unsigned char dailySchedule ); -int Util_getDoWThisMonth( TSC_SCHED_ELEM *pSchedElem, int dom ); -int Util_numDaysInSchedule( unsigned char dailySchedule ); - -char *Util_getDailyScheduleString( CNTL_SCHED_STRUCT *pTimeStruct ); -char *Util_getWeeklyScheduleString( CNTL_SCHED_STRUCT *pTimeStruct); -char *Util_getDayString( int dayOfWeek ); - -int TSCgetNumericSuffix(char* input); -char * TSCinterruptReasonToString( TSC_SCHED_MISS_REASON reason ); - -#endif
--- a/ICE-Application/src/Utilities/utilities.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +0,0 @@ -/****************************************************************************** - * File: utilities.cpp - * Description: - ******************************************************************************/ -#include <string.h> -#include <stdarg.h> -#include <stdlib.h> -#include "mbed.h" -#include "global.h" -#include "cJSON.h" -#include "ICELog.h" -#include "utilities.h" - -// -// function: capture -// description: captures the output from the __heapstats function -// -static int capture(void* pBuffer, char const* pFormatString, ...) -{ - char* pStringEnd = (char*)pBuffer + strlen((char*)pBuffer); - va_list valist; - - va_start(valist, pFormatString); - return vsprintf(pStringEnd, pFormatString, valist); -} - -// -// function: Util_isSequenceSubControl -// description: -// -bool Util_isSequenceSubControl(std::string controlFile) -{ - return (controlFile.substr(0, 4) == "seq_") ? true : false; -} - -// -// function: Util_isVirtualOutput -// description: -// -bool Util_isVirtualOutput(std::string output) -{ - return (output.substr(0, 2) == "v_" ) ? true : false; -} - -// -// function: getHeapData -// description: cleaned version of __heapstats() -// -// @param[in] none -// @param[out] none -// @return none -// -std::string Util_getHeapData(void) -{ -#ifndef __ICCARM__ - char data[256]; - memset(data, 0, sizeof(data)); - __heapstats(capture, data); - std::string msg = data; - std::string::size_type pos = 0; - while ((pos = msg.find("\n", pos)) != std::string::npos) { - msg.erase(pos, std::string::npos); - } - return msg; -#endif -} - -// -// function: getLastBootTime -// description: read the boot.time file and return the timestamp -// -// @param[in] none -// @param[out] none -// @return none -// -std::string Util_getLastBootTime(void) -{ -#ifdef MDOT_ICE - mDot::mdot_file file = GLOBAL_mdot->openUserFile("boot.time", mDot::FM_RDONLY); - if ( file.fd < 0 ) { - logError("%s: failed to open boot.time", __func__); - return "error"; - } - char *data_buf = (char*) malloc(file.size); - bool rc = GLOBAL_mdot->readUserFile(file, data_buf, file.size); - if ( rc != true ) { - logError("%s: failed to read boot.time", __func__); - free(data_buf); - GLOBAL_mdot->closeUserFile(file); - return "error"; - } - std::string bootTime = data_buf; - - free(data_buf); - GLOBAL_mdot->closeUserFile(file); - return bootTime; -#endif - return 0; -} - -std::string Util_GetFileById(const std::string id) -{ - // loop through all the files looking for a matching ID - -} - -std::string Util_GetIdbyFile(const std::string filename) -{ - char buf[MAX_FILE_SIZE]; - std::string id = ""; - int n = GLOBAL_mdot->readUserFile(filename.c_str(), (void*) buf, sizeof(buf)); - if ( n != sizeof(buf) ) { - logError("%s: error reading %s", __func__, filename.c_str()); - return id; - } - cJSON *root = cJSON_Parse(buf); - if ( !cJSON_HasObjectItem(root, "id") ) { - logError("%s: failed to find id in file %s\n", __func__, filename.c_str()); - } - id = cJSON_GetObjectItem(root, "id")->valuestring; - cJSON_Delete(root); - return id; -}
--- a/ICE-Application/src/Utilities/utilities.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -#ifndef UTILITIES_H -#define UTILITIES_H - -#include <string> - -bool Util_isVirtualOutput(std::string); -std::string Util_getHeapData(void); -std::string Util_getLastBootTime(void); -bool Util_isSequenceSubControl(std::string); - -#endif \ No newline at end of file
--- a/ICE-Application/src/Utilities/v7_c_calls.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,98 +0,0 @@ -#include "global.h" -#include <stdio.h> -#include <string> -#include <sstream> -#include <iostream> -#include "v7_c_calls.h" -#include "utilities.h" -#include "ModbusMasterApi.h" -#include "cJSON.h" -#include "ConfigurationHandler.h" - -#ifdef EXECUTE_SCRIPT - -char FileBuffer[MAX_FILE_SIZE]; - -enum v7_err js_modifySetpoint(struct v7 *v7, v7_val_t *res) -{ - std::ostringstream filename; - - // id of control i.e. INH_TRA_01 - v7_val_t arg0 = v7_arg(v7, 0); - const char *arg0_str = v7_get_cstring(v7, &arg0); - // setpoint - v7_val_t arg1 = v7_arg(v7, 0); - const char *arg1_str = v7_get_cstring(v7, &arg1); - - filename << CONTROL_SP_STR << arg0_str << ".json"; - - printf("%s:%d: filename = %s\r\n",__func__, __LINE__, filename.str().c_str() ); - - // read the file - bool rc = GLOBAL_mdot->readUserFile(filename.str().c_str(), (void*)FileBuffer, MAX_FILE_SIZE); - if ( rc != true ) { - printf("%s:%d: Failed to read %s\r\n",__func__, __LINE__, filename.str().c_str()); - return V7_INTERNAL_ERROR; - } - - cJSON * root = cJSON_Parse(FileBuffer); - strncpy( cJSON_GetObjectItem(root,"setpoint")->valuestring, arg1_str, (strlen(arg1_str)-1) ); - std::string s = cJSON_PrintUnformatted(root); - cJSON_Delete(root); - - printf("%s:%d: writing back: %s\r\n",__func__, __LINE__, s.c_str()); - - rc = GLOBAL_mdot->saveUserFile(filename.str().c_str(), (void*)s.c_str(), MAX_FILE_SIZE); - if ( rc != true ) { - printf("%s:%d: Failed to write %s\r\n",__func__, __LINE__, filename.str().c_str()); - return V7_INTERNAL_ERROR; - } - return V7_OK; -} - -enum v7_err js_getTime(struct v7 *v7, v7_val_t *res) -{ - time_t curr_sec = time(NULL); -// printf("%s:%d: time=%ld\r\n", __func__, __LINE__, curr_sec ); - *res = v7_mk_number(v7, curr_sec); - return V7_OK; -} - -enum v7_err js_setRegister(struct v7 *v7, v7_val_t *res) -{ - v7_val_t argv0 = v7_arg(v7, 0); - if( v7_is_string(argv0) ) { -// printf("first param is a string\r\n"); - } - const char *arg_str = v7_get_cstring(v7, &argv0); - double arg1 = v7_get_double(v7, v7_arg(v7, 1)); - (void)ScriptWriteRegister( arg_str, arg1 ); -// printf("%s:%d: string=%s, value=%2.2f\r\n", __func__, __LINE__, arg_str, arg1 ); - - *res = v7_mk_number(v7, 1); - return V7_OK; -} - -enum v7_err js_getRegister(struct v7 *v7, v7_val_t *res) -{ - v7_val_t argv0 = v7_arg(v7, 0); - if( v7_is_string(argv0) ) { -// printf("first param is a string\r\n"); - } - const char *arg_str = v7_get_cstring(v7, &argv0); - double reg_value = ScriptReadRegister( arg_str ); -// printf("%s:%d: string=%s, value=%2.2f\r\n", __func__, __LINE__, arg_str, reg_value ); - - *res = v7_mk_number(v7, reg_value); - return V7_OK; -} - -bool v7_Register_C_Calls(struct v7 *v7) -{ - v7_set_method(v7, v7_get_global(v7), "getTime", &js_getTime); - v7_set_method(v7, v7_get_global(v7), "setRegister", &js_setRegister); - v7_set_method(v7, v7_get_global(v7), "getRegister", &js_getRegister); - v7_set_method(v7, v7_get_global(v7), "modifySetpoint", &js_modifySetpoint); - return true; -} -#endif \ No newline at end of file
--- a/ICE-Application/src/Utilities/v7_c_calls.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -#ifndef V7_C_CALLS_H -#define V7_C_CALLS_H - -#include "v7.h" - -bool v7_Register_C_Calls(struct v7 *v7); - -#endif \ No newline at end of file
--- a/ICE-Application/src/Utilities/v7_execute.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -#include <stdio.h> -#include "v7_execute.h" -#include "utilities.h" -#include "rtos.h" - -#ifdef EXECUTE_SCRIPT - -struct v7 *v7; -Mutex v7_mutex; - -void v7_Create_Engine(void) -{ - v7 = v7_create(); - v7_Register_C_Calls(v7); -} - -void v7_Load_Script(const char * scriptString) -{ - enum v7_err rcode; - v7_val_t result; - - osStatus mutex_ret = v7_mutex.lock(); - if( mutex_ret != osOK ) printf("%s:%d: did not lock mutex, ret=0x%x\r\n", __func__, __LINE__, mutex_ret); - rcode = v7_exec(v7, scriptString, &result); - mutex_ret = v7_mutex.unlock(); - if( mutex_ret != osOK ) printf("%s:%d: did not unlock mutex, ret=0x%x\r\n", __func__, __LINE__, mutex_ret); - if( rcode != V7_OK ) - { - printf("%s:%d: Failed to load script: %s (len=%d), ret=%d\r\n", __func__, __LINE__, scriptString, strlen(scriptString), rcode); - } -} - -void v7_Execute_Script( std::string Command, std::string argv[NUM_SCRIPT_ARGS] ) -{ - v7_val_t func, result, args; - double argv_array[NUM_SCRIPT_ARGS]; - - if( Command.size() == 0 ) { - return; - } -// printf("\r%s\r\n", Util_getHeapData().c_str()); - - memset( argv_array, 0, (sizeof(double) * NUM_SCRIPT_ARGS)); - for( int i=0; i<NUM_SCRIPT_ARGS; i++ ) { - if( argv[i].size() != 0 ) { - argv_array[i] = RegisterValueMap[argv[i]].float_value; - } - } - -// printf("Command.size=%d, Command=%s(%s=%2.2f,%s=%2.2f,%s=%2.2f,%s=%2.2f)\r\n", Command.size() ,Command.c_str(), argv[0].c_str(), argv_array[0], argv[1].c_str(), argv_array[1], argv[2].c_str(), argv_array[2], argv[3].c_str(), argv_array[3]); - - osStatus mutex_ret = v7_mutex.lock(); - if( mutex_ret != osOK ) printf("%s:%d: did not lock mutex, ret=0x%x\r\n", __func__, __LINE__, mutex_ret); - - func = v7_get(v7, v7_get_global(v7), Command.c_str(), Command.size()); - - args = v7_mk_array(v7); - for( int i=0; i<NUM_SCRIPT_ARGS; i++ ) { - if( argv[i].size() != 0 ) { -// printf("Pushing %s\r\n", argv[i].c_str()); - v7_array_push(v7, args, v7_mk_string(v7, argv[i].c_str(), argv[i].size(), false)); - } - } - - if (v7_apply(v7, func, V7_UNDEFINED, args, &result) == V7_OK) { -// printf("Result: %g\r\n", v7_get_double(v7, result)); - } else { - printf("Error while calling %s\r\n", Command.c_str()); - } - - mutex_ret = v7_mutex.unlock(); - if( mutex_ret != osOK ) printf("%s:%d: did not unlock mutex, ret=0x%x\r\n", __func__, __LINE__, mutex_ret); -} -#endif \ No newline at end of file
--- a/ICE-Application/src/Utilities/v7_execute.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -#ifndef V7_EXECUTE_H -#define V7_EXECUTE_H - -#include "global.h" -#include "v7.h" -#include "v7_c_calls.h" - -void v7_Create_Engine(void); -void v7_Load_Script(const char * scriptString); -void v7_Execute_Script( std::string Command, std::string argv[NUM_SCRIPT_ARGS] ); - -#endif \ No newline at end of file
--- a/ICE-Application/src/add-ons/BLE/inc/ble_init.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ - -/** - ****************************************************************************** - * @file ble_types.h - * @author Happiesstminds Firmware Team - * @version v1.0 - * @date 4-Oct-2016 - * @brief - * - ****************************************************************************** - * @attention - * - * - ****************************************************************************** - */ - -#ifndef _BLE_INIT_H -#define _BLE_INIT_H - -class BLE_INIT -{ - public: - // Member functions declaration - uint8_t SendBleData(uint8_t *tx_buf, uint8_t len); - uint8_t InitBLEModule(void); - uint8_t SetDeviceName(void); - uint8_t SetAdvertisingData(void); - uint8_t SendInitBleCommand(const char *device_name); -}; - - - - -#endif -/******************************************************************************/ -/* END OF FILE */ -/******************************************************************************/ \ No newline at end of file
--- a/ICE-Application/src/add-ons/BLE/inc/ble_main.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/** - ****************************************************************************** - * @file ble_types.h - * @author Happiesstminds Firmware Team - * @version v1.0 - * @date 4-Oct-2016 - * @brief - * - ****************************************************************************** - * @attention - * - * - ****************************************************************************** - */ - -#ifndef _BLE_MAIN_H -#define _BLE_MAIN_H - -uint8_t PollBLEEvents(void); - -typedef void (*ble_data_ready_callback_t) (uint8_t *rx_data, uint8_t data_length); -void BleDataRxCbRegister(ble_data_ready_callback_t data_rx_callback); -class BLE_FILE -{ - public: - // Member functions declaration - uint8_t ConfigureBLEDevice(const char * device_name); - void SendFile(uint8_t* json_file,uint8_t len); - bool GetBleConnectionState(void); -}; - - -#define COMMAND_LENGTH 4 -#define DATA_LENGTH 0 -#define FAILURE 0 -#define MAX_PAYLOAD_BYTES 20 - -#endif -/******************************************************************************/ -/* END OF FILE */ -/******************************************************************************/ \ No newline at end of file
--- a/ICE-Application/src/add-ons/BLE/inc/ble_msg_handler.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -/** - ****************************************************************************** - * @file ble_types.h - * @author Happiesstminds Firmware Team - * @version v1.0 - * @date 4-Oct-2016 - * @brief - * - ****************************************************************************** - * @attention - * - * - ****************************************************************************** - */ - -#ifndef _BLE_MSG_HANDLER_H -#define _BLE_MSG_HANDLER_H -#include "ble_main.h" - -void ProcessBleRxEvents(uint8_t *spi_rcv_array); -#endif -/******************************************************************************/ -/* END OF FILE */ -/******************************************************************************/ \ No newline at end of file
--- a/ICE-Application/src/add-ons/BLE/inc/ble_spi.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/** - ****************************************************************************** - * @file ble_types.h - * @author Happiesstminds Firmware Team - * @version v1.0 - * @date 4-Oct-2016 - * @brief - * - ****************************************************************************** - * @attention - * - * - ****************************************************************************** - */ - -#ifndef _BLE_SPI_H -#define _BLE_TYPES_H - -#define BLE_SOF_CMD 0x01 -#define BLE_EOT_CMD 0x04 - -#define BLE_INIT_CMD 0xA1 -#define BLE_START_ADV_CMD 0xA2 -#define BLE_CONNECTION_EVNT_CMD 0xA3 -#define BLE_START_OF_FILE 0xA4 -#define BLE_SEND_DATA_CMD 0xA5 -#define BLE_END_OF_FILE 0xA6 -#define BLE_REC_DATA_CMD 0xA7 -#define BLE_DISCONNECTION_EVNT_CMD 0xA8 -#define BLE_ACK_CMD 0xAA - -#include "ble_main.h" - -uint8_t WriteSpiData(uint8_t * p_buffer, uint16_t length); -uint8_t InitSpiMaster(void); -uint8_t ReadSpiData(uint8_t *); -#endif - -/******************************************************************************/ -/* END OF FILE */ -/******************************************************************************/ \ No newline at end of file
--- a/ICE-Application/src/add-ons/BLE/src/ble_init.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,154 +0,0 @@ -/** - ****************************************************************************** - * @file ble_init.cpp - * @author Happiesstminds Firmware Team - * @version v1.0 - * @date 4-Oct-2016 - * @brief - * - ****************************************************************************** - * @attention - * - * - ****************************************************************************** - */ - -/******************************************************************************/ -/* Include Files*/ -/******************************************************************************/ -#include "mbed.h" -#include "ble_main.h" -#include "ble_spi.h" -#include "ble_msg_handler.h" -#include "ble_init.h" -/******************************************************************************/ -/* Local Defines */ -/******************************************************************************/ - -/******************************************************************************/ -/* Global Functions */ -/******************************************************************************/ - -/***************************************************************************** - * Function: InitBLEModule() - * Description: initialise GPIo and SPI - * - * @param BLE SPI Command - * @return status - *****************************************************************************/ -uint8_t BLE_INIT :: InitBLEModule(void) -{ - uint8_t status = SUCCESS; - - /*TODO - wait(3) second delay added to make sure that Nano BLE initialised first - after a power on reset.Within 3 seconds, expecting Nano BLE init is - succesfull and waiting for the commands from mDOt. - */ - - wait(3); - printf("\rInitializing BLE..."); - status = InitSpiMaster(); - if ( status != SUCCESS ) { - printf("failed %d)\n\r\n.", status); - } else { - printf("success.\n\r\n"); - } - return status; -} - - - -/***************************************************************************** - * Function: SendCommand() - * Description: Send command to BLE - * - * @param BLE SPI Command - * @return none - *****************************************************************************/ - -static void SendCommand(uint8_t command) -{ - uint8_t packet[4]; - - packet[0]= BLE_SOF_CMD; - packet[1]= command; - packet[2]= DATA_LENGTH; - packet[3]= BLE_EOT_CMD; - WriteSpiData(packet,COMMAND_LENGTH); -} - - -/***************************************************************************** - * Function: send BLE init command() - * Description: Send command to BLE - * - * @param none - * @return uint8_t status - *****************************************************************************/ -uint8_t BLE_INIT :: SendInitBleCommand(const char *device_name) -{ - uint8_t packet[50]; - uint8_t len =strlen(device_name); - packet[0]= BLE_SOF_CMD; - packet[1]= BLE_INIT_CMD; - packet[2]= len; - if(len>31) - { - /*maximum allowded device name length is 31*/ - len =31; - } - memcpy(&packet[3], device_name, len); - packet[len + 3]= BLE_EOT_CMD; - WriteSpiData(packet,(len + 4)); - return 0; -} - -/***************************************************************************** - * Function: StartAdvertisingData() - * Description: Send startAdvertisingData Command - * - * @param none - * @return uint8_t status - *****************************************************************************/ -uint8_t StartAdvertisingData(void) -{ - uint8_t status =SUCCESS; - SendCommand(BLE_START_ADV_CMD); - return status; -} - -/***************************************************************************** - * Function: SendBleData() - * Description: Send data over BLE (Max 20 bytes) - * - * @param txBuf - * @param data length - * @return uint8_t status - *****************************************************************************/ -uint8_t BLE_INIT :: SendBleData(uint8_t *tx_buf, uint8_t len) -{ - uint8_t packet[24]; - uint8_t status = SUCCESS; - uint8_t packetlength; - packetlength = len + 4; - - if(len<=20) - { - packet[0]= BLE_SOF_CMD; - packet[1]= BLE_SEND_DATA_CMD; - packet[2]= len; - - memcpy(&packet[3], tx_buf, len); - packet[len + 3]= BLE_EOT_CMD; - WriteSpiData(packet, packetlength); - } - else - { - status = FAILURE; - } - return status; -} -/******************************************************************************/ -/* END OF FILE */ -/******************************************************************************/ \ No newline at end of file
--- a/ICE-Application/src/add-ons/BLE/src/ble_main.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,174 +0,0 @@ -/** - ****************************************************************************** - * @file ble_uart.cpp - * @author Happiesstminds Firmware Team - * @version v1.0 - * @date 4-Oct-2016 - * @brief - * - ****************************************************************************** - * @attention - * - * - ****************************************************************************** - */ - -/******************************************************************************/ -/* Include Files*/ -/******************************************************************************/ -#include "mbed.h" -#include "ble_main.h" -#include "ble_spi.h" -#include "ble_msg_handler.h" -#include "ble_init.h" - - - -/******************************************************************************/ -/* Global Functions */ -/******************************************************************************/ -BLE_INIT BLE_INIT; -extern volatile bool isDeviceConnected; -uint8_t temp_buf[30]; -uint8_t dummy_bytes; -/******************************************************************************* - * Function: ConfigureBLEDevice() - * Description: Initialise and configure BLE - * - * @param none - * @return none - ******************************************************************************/ - -uint8_t BLE_FILE :: ConfigureBLEDevice(const char * device_name) -{ - - uint8_t status = SUCCESS; - BLE_INIT.InitBLEModule(); //initialising BLE SPI GPIO etc. - /* - The below function called 3 times to makse sure that mDot board - initialise properly.The nano BLE code handle this by making sure that - initialisation happening only once. - */ - BLE_INIT.SendInitBleCommand(device_name); //initializing BLE - BLE_INIT.SendInitBleCommand(device_name); //initializing BLE - BLE_INIT.SendInitBleCommand(device_name); //initializing BLE - return status; -} - -/***************************************************************************** - * Function: GetBleConnectionState() - * Description: check whether device is connected or not - * - * @param none - * @return uint8_t status(connected or disconnected) - *****************************************************************************/ - -bool BLE_FILE :: GetBleConnectionState(void) -{ - return isDeviceConnected; -} - -/***************************************************************************** - * Function: SendSOF() - * Description: This function send Start of File transfer frame - * - * @param tx_buf -* @param data length - * @return none - *****************************************************************************/ -static void SendSOF(void) -{ - uint8_t packet[24]; - packet[0] =BLE_SOF_CMD; - packet[1] =BLE_START_OF_FILE; - packet[2] =MAX_PAYLOAD_BYTES; - packet[3] ='S'; - packet[4] ='O'; - packet[5] ='F'; - memset(&packet[6], '@', 17); - packet[23]=BLE_EOT_CMD; - WriteSpiData(packet,24); -} - -/***************************************************************************** - * Function: ClearBuffer - * Description: This function clear the receive buffer. This is to make - * sure that receive buffer is empty.Sending 20 dummy bytes - (@)to BLE device. - * @param void - * @return none - *****************************************************************************/ -static void ClearBuffer(void) -{ - uint8_t packet[24]; - packet[0] =BLE_SOF_CMD; - packet[1] =0xFF; - packet[2] =MAX_PAYLOAD_BYTES; - memset(&packet[3], '@', 20); - packet[23]=BLE_EOT_CMD; - WriteSpiData(packet,24); -} -/***************************************************************************** - * Function: SendEOF() - * Description: This function send End of File transfer frame - * - * @param none - * @return none - *****************************************************************************/ -static void SendEOF(void) -{ - uint8_t packet[24]; - - packet[0] =BLE_SOF_CMD; - packet[1] =BLE_END_OF_FILE; - packet[2] =MAX_PAYLOAD_BYTES; - packet[3] ='E'; - packet[4] ='O'; - packet[5] ='F'; - memset(&packet[6], '@', 17); - packet[23]=BLE_EOT_CMD; - WriteSpiData(packet,24); -} - -/***************************************************************************** - * Function: SendFile() - * Description: This function receive the json file data and transmit - data over BLE - * - * @param json file data - * @param data length - * @return none - *****************************************************************************/ -void BLE_FILE :: SendFile(uint8_t* json_file,uint8_t len) -{ - uint8_t num_writes,rem_bytes,start_index =0; - - num_writes = ((len)/MAX_PAYLOAD_BYTES); - rem_bytes =((len)%MAX_PAYLOAD_BYTES); - /* Clearing the buffer to make sure that transmit buffer is empty*/ - ClearBuffer(); - - SendSOF(); - - for(int packets=0;packets<(num_writes);packets++) - { - BLE_INIT.SendBleData(&json_file[start_index], MAX_PAYLOAD_BYTES); - start_index= start_index+MAX_PAYLOAD_BYTES; - } - if(rem_bytes) - { - dummy_bytes = MAX_PAYLOAD_BYTES-rem_bytes; - memcpy(temp_buf,&json_file[start_index],rem_bytes); - /*Filling the remaining bytes with characcter '@' */ - memset(&temp_buf[rem_bytes],'@',dummy_bytes); - BLE_INIT.SendBleData(temp_buf, MAX_PAYLOAD_BYTES); - } - - SendEOF(); - -} - - -/******************************************************************************/ -/* END OF FILE */ -/******************************************************************************/ \ No newline at end of file
--- a/ICE-Application/src/add-ons/BLE/src/ble_msg_handler.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,127 +0,0 @@ -/** - ****************************************************************************** - * @file ble_uart.cpp - * @author Happiesstminds Firmware Team - * @version v1.0 - * @date 4-Oct-2016 - * @brief - * - ****************************************************************************** - * @attention - * - * - ****************************************************************************** - */ - -/******************************************************************************/ -/* Include Files*/ -/******************************************************************************/ - -#include "mbed.h" -#include "ble_msg_handler.h" -#include "ble_spi.h" -#include "ble_main.h" -#include "LoggerApi.h" -/******************************************************************************/ -/* Defines */ -/******************************************************************************/ -#define BLE_MAX_FILE_SIZE 500 -#define HEADER_LENGTH 5 -volatile bool isDeviceConnected; -static ble_data_ready_callback_t data_ready_cb; - -/****************************************************************************** - * Function: BLE data receive callback - * Description: Function used to register ble data receive callback - * - * @param ble_data_ready_callback_t - * @return none - *****************************************************************************/ -void BleDataRxCbRegister(ble_data_ready_callback_t data_rx_callback) -{ - data_ready_cb = data_rx_callback; -} - -/***************************************************************************** - * Function: ProcessBleRxEvents() - * Description: Process BLE data - * - * @param spi_rcv_array - * @return none - *****************************************************************************/ -void ProcessBleRxEvents(uint8_t *spi_rcv_array) -{ - static bool file_receiving_flag = false; - static uint8_t json_array[BLE_MAX_FILE_SIZE]; - static uint16_t file_index,file_size; - - if(spi_rcv_array !=NULL) - { - if (spi_rcv_array[0] == BLE_SOF_CMD) { - switch (spi_rcv_array[1]) - { - case BLE_CONNECTION_EVNT_CMD: - printf("Mobile device connected...\n\r"); - isDeviceConnected =true; - DeviceConnectedLoggerApi(isDeviceConnected); - break; - - case BLE_REC_DATA_CMD: - - if(file_receiving_flag ==true) - { - /*reassembling packets */ - memcpy(&json_array[file_index],&spi_rcv_array[3], (spi_rcv_array[2]-HEADER_LENGTH)); - - file_index+= (spi_rcv_array[2]-HEADER_LENGTH); //((spi_rcv_array[2]-5) contains packet length excluding headers - if(file_index > file_size) - { - data_ready_cb(json_array, file_size); - file_index =0; - file_size =0; - file_receiving_flag = false; - } - } - break; - - case BLE_DISCONNECTION_EVNT_CMD: - printf("Mobile device disconnected...\n\r"); - isDeviceConnected =false; - DeviceConnectedLoggerApi(isDeviceConnected); - break; - - case BLE_START_OF_FILE: - - file_index =0; - file_receiving_flag = true; - - /*extract the json string length from SOF. */ - file_size = ((spi_rcv_array[3]<<8)|spi_rcv_array[4]); - printf("Received file size is %d \n\r",file_size); - if(file_size>BLE_MAX_FILE_SIZE) - { - printf("Received file larger than buffer (len=%d)\r\n", BLE_MAX_FILE_SIZE ); - file_receiving_flag = false; - file_size =0; - } - - break; - - case BLE_END_OF_FILE: - /* Reset the File Parameters.Currently not using */ - file_receiving_flag = false; - break; - - default: - printf("Unknown ID\n\r"); - break; - } - } else { - printf("SOF not found\n\r"); - } - } -} - -/******************************************************************************/ -/* END OF FILE */ -/******************************************************************************/
--- a/ICE-Application/src/add-ons/BLE/src/ble_spi.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,119 +0,0 @@ -/** - ****************************************************************************** - * @file ble_uart.cpp - * @author Happiesstminds Firmware Team - * @version v1.0 - * @date 4-Oct-2016 - * @brief - * - ****************************************************************************** - * @attention - * - * - ****************************************************************************** - */ - -/******************************************************************************/ -/* Include Files*/ -/******************************************************************************/ -#include "mbed.h" -#include "ble_msg_handler.h" -/******************************************************************************/ -/* Local Defines */ -/******************************************************************************/ -uint8_t rx_buf[24]; -//SPI spimaster(SPI1_MOSI, SPI1_MISO, SPI1_SCK); -//DigitalOut cs(SPI1_CS); - -/******************************************************************************/ -/* Global Functions */ -/******************************************************************************/ - - -/***************************************************************************** - * Function: InitSpiMaster() - * Description: InitSpiMaster - * - * @param None - * @return none - *****************************************************************************/ -uint8_t InitSpiMaster(void) -{ - uint8_t status = SUCCESS; - // Chip must be deselected -// cs = 1; - // Setup the spi for 8 bit data, high steady state clock, - // second edge capture, with a 1MHz clock rate -// spimaster.format(8, 0); - // spimaster.frequency(1000000); -// spimaster.frequency(1000000); - return status; -} - -/***************************************************************************** - * Function: WriteSpiData() - * Description: WriteSpiData - * - * @param pointer to buffer - * @param length - * @return none - *****************************************************************************/ -uint8_t WriteSpiData(uint8_t * p_buffer, uint16_t length) -{ - uint8_t i; - uint8_t status = SUCCESS; -// int response; //need to be removed - // Select the device by seting chip select low - - for(i=0; i<length; i++) { -// cs = 0; -// response = spimaster.write(p_buffer[i]); -// cs = 1; - wait_us(600); - } - - return status; -} - -/***************************************************************************** - * Function: PollBLEEvents() - * Description: Polls for BLE events - * @param pointer to rx_buf - * @return none - *****************************************************************************/ - -uint8_t PollBLEEvents(void) -{ - uint8_t i =0; - uint8_t msg_len; - -// cs = 0; -// rx_buf[0] = spimaster.write(0xAA); -// cs = 1; - wait_us(600); - - if(rx_buf[0]==0x01) { -// cs = 0; - // rx_buf[1] = spimaster.write(0xAA); -// cs = 1; - wait_us(600); -// cs = 0; -// rx_buf[2] = spimaster.write(0xAA); -// cs = 1; - wait_us(600); - - msg_len = rx_buf[2]; - for(i = 3; i < msg_len; i++) { -// cs = 0; -// rx_buf[i] = spimaster.write(0xFF); -// cs = 1; - wait_us(600); - } - - ProcessBleRxEvents(rx_buf); - } - return rx_buf[0]; -} -/******************************************************************************/ -/* END OF FILE */ -/******************************************************************************/
--- a/ICE-Application/src/add-ons/ConfigFS/ConfigFs.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -#include "ConfigFs.h" -#include "mbed.h" -#include "mfs.h" - -bool ConfigFs::saveUserFile(const char *filename, void *file_buf, int file_size) -{ -// printf("Writing filename=%s, size=%d\r\n", filename, file_size); - mfs fs0(0xA0); - fs0.createFile((char *)filename); - file *fp = new file(&fs0, (char *)filename, AWRITE); - fp->write((char *)file_buf, file_size); - if( fp->flush() != 0 ) { - printf("Flush Failed\r\n"); - } - delete fp; - return true; -} - -bool ConfigFs::readUserFile(const char *filename, void *file_buf, int file_size) -{ - mfs fs0(0xA0); -// printf("%s, len=%d\r\n", filename, strlen(filename)); - file *fp = new file(&fs0, (char *)filename, AWRITE); - fp->read((char *)file_buf, file_size); - delete fp; -// printf("File Read: %s; bytes=%d, len=%d\n\r", (char *)file_buf, file_size, strlen((char *)file_buf)); - return true; -} - -bool ConfigFs::deleteUserFile(const char *filename) -{ - mfs fs0(0xA0); - fs0.removeFile((char *)filename); - return true; -} - -std::vector<std::string> ConfigFs::listUserFiles() -{ - mfs fs0(0xA0); - unsigned int n; - char found_file[FILENAME_LENGTH]; - std::vector<std::string> filelist; - - n=0; - while (1) { - if (fs0.findNextFile(n, found_file, &n) == 0 ) { - filelist.push_back(found_file); - } else { - break; // Reach end of fs - } - n++; - } - return filelist; - -} \ No newline at end of file
--- a/ICE-Application/src/add-ons/ConfigFS/ConfigFs.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -#ifndef CONFIGFS_H -#define CONFIGFS_H - -#include <vector> -#include <map> -#include <string> - -class ConfigFs -{ -public: - bool saveUserFile(const char *FileName, void *file_buf, int file_size); - bool readUserFile(const char *FileName, void *file_buf, int file_size); - bool deleteUserFile(const char *filename); - std::vector<std::string> listUserFiles(); -}; - -#endif \ No newline at end of file
--- a/ICE-Application/src/add-ons/ConfigFS/mfs.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,686 +0,0 @@ -/** @file mfs.cpp */ -/*CPP************************************************************************** - * FILENAME : mfs.cpp * - * * - * DESCRIPTION : * - * mFS file system implementation for mBED with external I2C EEEPROM. * - * * - * AUTHOR : Olli Vanhoja START DATE : 2011-02-21 * - *****************************************************************************/ - -#include "mbed.h" -#include "mfs.h" -#include "i2c_eeprom.h" - -#define BLOCK_LLEN 2 /**< Block number link length in bytes */ -#define RB 1+2*BLOCK_LLEN /**< Reseved bytes per block (1 attrb B, 2 B for next/prev pointers */ -#define MFS_I2C_SPEED 100000 /**< I2C bus speed in Hz */ -#define DEBUG /**< Adds extra safety in reading and writing */ - -mfs::mfs(int i2c_address) -{ - mem = new i2c_eeprom(i2c_address, MFS_I2C_SPEED); -} - -char mfs::read(char *data, uint32_t block, uint32_t byte, uint32_t n) -{ - // Faster reading without DEBUG mode - #ifdef DEBUG - if ((byte+n-1 >= BS)) - return 1; - #endif - mem->read(BS*block+byte, n, data); - - return 0; -} - -char mfs::write(char *data, uint32_t block, uint32_t byte, uint32_t n) -{ - // Faster writing without DEBUG mode - #ifdef DEBUG - if (byte+n >= BS) - return 1; - #endif - mem->write(data, BS*block+byte, n); - - return 0; -} - -char mfs::getNextFreeBlock(uint32_t *blockOut) -{ - // Locate free block by seeking EERPOM - char cFlags[1]; - - for (*blockOut=0; *blockOut < BC; (*blockOut)++) - { - read(cFlags, *blockOut, 0, 1); - if (cFlags[0] == 0x04) - break; - if (*blockOut >= BC-1) - return 1; - } - - return 0; -} - -char mfs::findNextFile(uint32_t block, char *filenameOut, uint32_t *blockOut) -{ - uint32_t i=block; - char cFlags[1]; - - while (i < BC) - { - read(cFlags, i, 0, 1); - - if ((cFlags[0] & 0x8C) == 0x8C) - break; // File found - else - i++; - } - - if(i == BC) - { - strcpy(filenameOut, ""); - return 1; // Empty fs - } - - // Read filename - read(filenameOut, i, RB, FILENAME_LENGTH); - *blockOut = i; // Return block number - return 0; -} - -char mfs::getFirstBlockOfFile(char filename[FILENAME_LENGTH], uint32_t *blockOut) -{ - *blockOut=0; - char tmpFilename[FILENAME_LENGTH]=""; - - while (1) - { - if (findNextFile(*blockOut, tmpFilename, blockOut) == 0) - { - if(strcmp(tmpFilename, filename) == 0) - return 0; // File exists - } - else return 1; // File doesn't exist - (*blockOut)++; - } -} - -char mfs::createFile(char filename[FILENAME_LENGTH]) -{ - char tmpFilename[FILENAME_LENGTH]; - uint32_t n; - uint32_t fb; - - for (n=0; n < BC; n++) - { - if(findNextFile(n, tmpFilename, &n) == 0) - { - if(strcmp(tmpFilename, filename) == 0) - return 1; // File exist - } - else break; // We already reached the edge of the universe - n++; - } - - if(getNextFreeBlock(&fb) != 0) - return 2; // Out of space - - char cData[RB+FILENAME_LENGTH+1]; - cData[0] = '\xCC'; // Necessary flags for a file - cData[1] = '\0'; // No more blocks yet - cData[2] = '\0'; - cData[3] = '\0'; // First block so there is no prev blocks - cData[4] = '\0'; - cData[RB+FILENAME_LENGTH] = mEOF; // Set EOF at the begining - - for (char i=0; i < FILENAME_LENGTH; i++) - cData[RB+i] = filename[i]; - - // Create file - write(cData, fb, 0, RB+FILENAME_LENGTH+1); - - return 0; -} - -char mfs::removeFile(char filename[FILENAME_LENGTH]) -{ - uint32_t block; - char cData[RB-BLOCK_LLEN]; - char cDataNew[RB] = {'\x04', '\0', '\0', '\0', '\0'}; - uint32_t i=0; - - // Check if file exists - if (getFirstBlockOfFile(filename, &block) != 0) - return 1; // File not found - - read(cData, block, 0, RB-BLOCK_LLEN); - - // Check credentials - if (((cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3) & 0x04) != 0) - return 2; // RO file - - // Clear blocks reserved by the file - while(1) - { - write(cDataNew, block, 0, RB); - if ((cData[0] & 0x4C) == 0x4C) - break; // End of file found - else block = (uint32_t)(cData[1])<<8|cData[2]; // Set next block number - i++; - if (i > BC) - return 1; // fs is corrupted - read(cData, block, 0, RB-BLOCK_LLEN); - } - - return 0; // Everything went better than expected -} - -char mfs::renameFile(char oldFilename[FILENAME_LENGTH], char newFilename[FILENAME_LENGTH]) -{ - uint32_t block; - char cData[1]; - - // Check if file exists - if (getFirstBlockOfFile(oldFilename, &block) != 0) - return 1; // File not found - - // Check credentials - read(cData, block, 0, 1); - char flags = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3); - if ((flags & 0x04) != 0) - return 2; // RO file - - write(newFilename, block, RB, FILENAME_LENGTH); - - return 0; // Everything went better than expected -} - -char mfs::setFileFlags(char *flags, char filename[FILENAME_LENGTH]) -{ - /* RO|HIDDEN|LOCK * - * H L */ - - uint32_t n; - char cData[1] = {'\0'}; - char cFlags; - - // Check if file exists - if (getFirstBlockOfFile(filename, &n) != 0) - return 1; // File not found - - read(cData, n, 0, 1); - cFlags = ((flags[0] & 0x01)|((flags[0] & 0x02) << 3)|((flags[0] & 0x04) << 3)); - cData[0] = cData[0] & (~0x31) | cFlags; - write(cData, n, 0, 1); - - return 0; -} - -char mfs::getFileFlags(char *flags, char filename[FILENAME_LENGTH]) -{ - /* RO|HIDDEN|LOCK * - * H L */ - - uint32_t n; - char cData[1] = {'\0'}; - - // Check if file exists - if (getFirstBlockOfFile(filename, &n) != 0) - return 1; // File not found - - read(cData, n, 0, 1); - flags[0] = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3); - - return 0; -} - -// Return number of free blocks -uint32_t mfs::free() -{ - uint32_t blocks=0; - uint32_t r; - char cFlags[1]; - - for (r=0; r < BC; r++) - { - read(cFlags, r, 0, 1); - if (cFlags[0] == 0x04) - blocks++; - if (r >= BC-1) - return blocks; - } - - return 0; -} - -uint32_t mfs::mkfs(bool createLabel) -{ - uint32_t iAddr = 0; - uint32_t i = 0; - uint32_t bad = 0; // For counting bad block headers - char cFlags[RB] = {'\0', '\0', '\0', '\0', '\0'}, o[1]; - - if (createLabel == true) - { - // Write Volume label - cFlags[0] = '\x0E'; - mem->write(cFlags, iAddr, RB); - iAddr = BS; - i = 1; - } - - cFlags[0] = '\x04'; - for (; i < BC; i++) - { - mem->write(cFlags, iAddr, RB); - mem->read(iAddr, 1, o); - if (o[0] != cFlags[0]) - bad++; - iAddr += BS; - } - - return bad; -} - -file::file(mfs *fs_ref, char filename[FILENAME_LENGTH], FileOpenMode operation) -{ - fMode = operation; - - fs = fs_ref; // Don't forget this :) - - uint32_t n; - char cData[1] = {'\0'}; - - // Check if file exists - if (fs->getFirstBlockOfFile(filename, &n) != 0) - error("Oops, file \"%s\" not found! n=0x%X", filename, n); // File not found - - fs->read(cData, n, 0, 1); - char flags = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3); - - if ((fMode != RO) && ((flags & 0x04) != 0)) - error("Oops, cant open in RW mode!"); - - // Store FBOF number - firstBlock = n; - currBlock = n; - blockPos = RB+FILENAME_LENGTH; // skip flags + pointers + filename - byteCount = 0; // First byte of the file - - // Initialize buffer - for (unsigned int i=0; i < BUF; i++) - buffer[i] = '\0'; - bufPos = 0; -} - -file::~file() -{ - flush(); -} - -char file::getBlockLink(BlockLinkType linkSelection, uint32_t *blockOut) -{ - char cData[1+BLOCK_LLEN]; - - if (linkSelection == NEXT) - { - // Fetch link to next block - fs->read(cData, currBlock, 0, 1+BLOCK_LLEN); - if ((cData[0] & 0x40) == 0) - { - *blockOut = ((uint32_t)(cData[1])) << 8; // Hbyte of next block link - *blockOut |= (uint32_t)cData[2]; // Lbyte of next block link - return 0; - } else return 1; // Already at last block - } else if (linkSelection == PREV) - { - if (currBlock != firstBlock) - { - fs->read(cData, currBlock, 1+BLOCK_LLEN, BLOCK_LLEN); - *blockOut = ((uint32_t)(cData[0])) << 8; // Hbyte of next block link - *blockOut |= (uint32_t)cData[1]; // Lbyte of next block link - return 0; - } else return 1; // Already at first block - } - - return 0; -} - -char file::removeFollowingBlocks(uint32_t block) -{ - char cData[RB-BLOCK_LLEN]; - char cDataNew[RB] = {'\x04', '\0', '\0', '\0', '\0'}; - uint32_t i=0; - - while(1) - { - fs->read(cData, block, 0, RB-BLOCK_LLEN); - fs->write(cDataNew, block, 0, RB); - if ((cData[0] & 0x4C) == 0x4C) - break; // End of file found - else block = (uint32_t)(cData[0])<<8|cData[1]; // Set next block number - i++; - if (i > BC) - return 1; // fs is corrupted - } - - return 0; -} - -void file::rewind() -{ - flush(); - currBlock = firstBlock; - blockPos = RB+FILENAME_LENGTH; // skip flags & pointers + filename - byteCount = 0; -} - -char file::rewind(uint32_t n) -{ - uint32_t i; - uint32_t block; - uint32_t varBlockOffset; - - flush(); // Check if flush is needed - - for (i=0; i < n; i++) - { - blockPos--; - byteCount--; - - // Change Block? - if (blockPos == firstBlock) - varBlockOffset = RB+FILENAME_LENGTH; - else - varBlockOffset = RB; - if ((blockPos < varBlockOffset) && (currBlock > firstBlock)) - { - // Fetch link to previous block - if(getBlockLink(PREV, &block) == 0) - { - currBlock = block; - blockPos = BS-1; // blockPos should be set at the end of block - } else { - blockPos++; - byteCount++; - return 1; // This is the first block and byte - } - } - } - - return 0; // OK -} - -char file::forward() -{ - return forward(1); -} - -char file::forward(uint32_t n) -{ - uint32_t i; - uint32_t block; // Next block number - char cData[1]; - - flush(); // Check if flush is needed - - for (i=0; i < n; i++) - { - // If this is empty file EOF will be at first byte - fs->read(cData, currBlock, blockPos, 1); - if (cData[0] == mEOF) - { - return 1; - } - - blockPos++; - byteCount++; - - // Change Block? - if (blockPos >= BS) - { - // Fetch link to next block - if (getBlockLink(NEXT, &block) == 0) - { - currBlock = block; - blockPos = RB; // Reset block position offset - } else { - blockPos--; - byteCount--; - return 1; // This is the last block & byte - } - } - fs->read(cData, currBlock, blockPos, 1); - if (cData[0] == mEOF) - { - rewind(1); // Get back to the byte before EOF - return 1; - } - } - - return 0; // OK -} - -char file::seek(uint32_t byte) -{ - if (byte > byteCount) - return forward(byte-byteCount); - else if (byte < byteCount) - return rewind(byteCount-byte); - else return 0; -} - -// Respects mEOF and automatically sets '\0' at the end of string -void file::read(char *data, uint32_t n) -{ - uint32_t i; - uint32_t block; - char cData[1]; - - if( flush() != 0 ) { - printf("(%s:%d): flush failed\r\n", __func__, __LINE__); - } - - for (i=0; i < n; i++) - { - // Change block? - if (blockPos >= BS-1) - { -// printf("(%s:%d): pos=%d, BS-1=%d\r\n", __func__, __LINE__, blockPos, BS-1); - // Fetch link to next block - if (getBlockLink(NEXT, &block) == 0) - { - currBlock = block; - blockPos = RB; // Reset block position offset - } else goto stop; - } - - // Read data - - fs->read(cData, currBlock, blockPos, 1); - if (cData[0] == mEOF) - { -// printf("(%s:%d): Found EOF: currBlock=%d, blockPos=%d\r\n", __func__, __LINE__, currBlock, blockPos); - stop: - data[i]='\0'; - return; - } else { - data[i] = cData[0]; -// printf("(%s:%d): blk=%d, pos=%d, char=%c\r\n", __func__, __LINE__, currBlock, blockPos, data[i]); - blockPos++; - byteCount++; - } - } - if (data[n-1] != '\0') { - printf("(%s:%d): Adding NULL\r\n", __func__, __LINE__ ); - data[n-1] = '\0'; - } -} - -// Ignores mEOF and doesn't set '\0' markings -void file::readBin(char *data, uint32_t n) -{ - uint32_t i; - uint32_t block; - char cData[1]; - - for (i=0; i < n; i++) - { - // Change block? - if (blockPos == BS-1) - { - // Fetch link to next block - if (getBlockLink(NEXT, &block) == 0) - { - currBlock = block; - blockPos = RB; // Reset block position offset - } else return; - } - - // Read data - fs->read(cData, currBlock, blockPos, 1); - data[i] = cData[0]; - - blockPos++; - byteCount++; - } -} - -// Always binary -char file::write(char *data, uint32_t n) -{ - if (fMode == RO) return 1; - - for (uint32_t i=0; i < n; i++) - { - // write to the buffer - buffer[bufPos] = data[i]; - bufPos++; - - // If the buffer is full then flush - if(bufPos == BUF) - { - printf("(%s:%d): flushing buffer: bufPos=%d\r\n", __func__, __LINE__, bufPos); - if(flush() != 0); - return 1; // Flush failed - } - } - - return 0; -} - -char file::flush() -{ - char cData[RB], cDataOB[RB-BLOCK_LLEN], c[1]; - uint32_t nextFree; - uint32_t i; - uint32_t leftSpaceEOF; - - bool destructiveFlag = false; // Set this true if there is any data to be removed - uint32_t destructiveBlock=0; // First block to be removed by DWRITE - - static bool fEOFow = false; /* END of file found while appending (and overwritten). - Must be static because we don't want to found many EOF's. */ - - - if (bufPos == 0) return 0; // File up-to date - if (fMode == RO) return 1; - - for (i=0; i <= bufPos; i++) - { - if( blockPos == 255 ) - { -// printf("%s:%d:\r\n",__func__, __LINE__); - } - - // Change Block? - if ((bufPos - i) == 1) - leftSpaceEOF = 1; - else - leftSpaceEOF = 0; - if (blockPos+1 >= BS-leftSpaceEOF) - { -// printf("(%s:%d): fetching unused block: currBlock=%d, blockPos=%d, bufPos=%d\r\n", __func__, __LINE__, currBlock, blockPos, bufPos ); - // Fetch new unused block number - if(fs->getNextFreeBlock(&nextFree) != 0) - return 2; // No free space left - - // Read flags from current block - fs->read(cDataOB, currBlock, 0, RB-BLOCK_LLEN); - - /* If destructive write is set then check if there is something - to be marked for removal */ - if (((cDataOB[0] & 0x40) != 0x40) && (fMode == DWRITE)) - { - destructiveFlag = true; - destructiveBlock = (uint32_t)cDataOB[1]<<8|cDataOB[2]; -// printf("(%s:%d): destructive flag set: currBlock=%d, blockPos=%d, bufPos=%d\r\n", __func__, __LINE__, currBlock, blockPos, bufPos ); - goto allocate_new_block; - } else if ((cDataOB[0] & 0x40) != 0x40) // fMode == AWRITE - { - // Update current block info - currBlock = (uint32_t)cDataOB[1]<<8|cDataOB[2]; - blockPos = RB; // Reset block position offset -// printf("(%s:%d): resetting block position: currBlock=%d, blockPos=%d, bufPos=%d\r\n", __func__, __LINE__, currBlock, blockPos, bufPos ); - } else // There is no block to append so we allocate a new one - { - allocate_new_block: - // Allocate new block for use - cData[0] = 0x4C; // New flags - cData[1] = '\0'; // Hbyte of Next Block link - cData[2] = '\0'; // Lbyte of Next Block link - cData[3] = (char)((currBlock & 0xff00) >> 8); // Hbyte of Prev Block link - cData[4] = (char)(currBlock & 0x00ff); // Lbyte of Prev Block link - fs->write(cData, nextFree, 0, RB); // Update Block Data - - // Link old block with new block - cDataOB[0] &= ~0x40; // Clear LBOF flag if set - cDataOB[1] = (char)((nextFree & 0xff00) >> 8); // Hbyte of Next Block link - cDataOB[2] = (char)(nextFree & 0x00ff); // Lbyte of Next Block link - fs->write(cDataOB, currBlock, 0, RB-BLOCK_LLEN); // Update Block Data - - // Update current block info - currBlock = nextFree; - blockPos = RB; // Reset block position offset - -// printf("(%s:%d): allocating new block: currBlock=%d, blockPos=%d, bufPos=%d\r\n", __func__, __LINE__, currBlock, blockPos, bufPos ); - } - } - - if (fMode == AWRITE) // Check if EOF is here - { - fs->read(c, currBlock, blockPos, 1); - if ((c[0] == mEOF) && (fEOFow == false)) - fEOFow = true; - } - - // Write file - c[0]=buffer[i]; - fs->write(c, currBlock, blockPos, 1); -// printf("(%s:%d): blk=%d, pos=%d, char=%c\r\n", __func__, __LINE__, currBlock, blockPos, c[0] ); - blockPos++; - byteCount++; - - // For fail safe, write EOF now - if ((fMode == DWRITE)||(fEOFow == true)) - { - char fs_data[] = { mEOF }; - fs->write(fs_data, currBlock, blockPos, 1); // Write mEOF -// printf("(%s:%d): Writing EOF, blk=%d, pos=%d, char=0x%x\r\n", __func__, __LINE__, currBlock, blockPos, fs_data[0] ); - } - } - - bufPos = 0; // Reset buffer position counter - - /* If destructive write flag is set - and there is data to be removed then remove data now */ - if (destructiveFlag == true) { - if(removeFollowingBlocks(destructiveBlock) != 0) { - return 3; - } - } - - return 0; -}
--- a/ICE-Application/src/add-ons/ConfigFS/mfs.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,332 +0,0 @@ -/** @file mfs.h */ -/*H**************************************************************************** - * FILENAME : mfs.h * - * * - * DESCRIPTION : * - * mFS file system implementation for mBED with external I2C EEEPROM. * - * * - * ---------------------------------------------------------------------------* - * "THE BEER-WARE LICENSE" (Revision 42): * - * <olli.vanhoja@gmail.com> wrote this file. As long as you retain this notice* - * you can do whatever you want with this stuff. If we meet some day, and you * - * think this stuff is worth it, you can buy me a beer in return Olli Vanhoja * - * ---------------------------------------------------------------------------* - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * * - * * - * Block Flags: * - * 7:FBOF Begining of file * - * 6:LBOF Last block of file * - * 5:RO Read only file (Used only with FBOF) * - * 4:HIDDEN Hidden file (Used only with FBOF) * - * 3:INUSE Block in use * - * 2:NBAD Bad block (INV) * - * 1:VOL Volume label (Used only with FBOF) * - * 0:LOCK Locked file (Used only with FBOF) * - * * - * AUTHOR : Olli Vanhoja START DATE : 2011-02-18 * - ****************************************************************************** - * - * CHANGES : - * - * VERSION DATE WHO DETAIL - * 0.1 2011-02-21 Olli Vanhoja Initial release version - * 0.2 2011-02-21 Olli Vanhoja Documentational comments added - * 0.3 2011-02-21 Olli Vanhoja * File::read issues fixed, rewind/forward - * functions improved - * * Added possibility change I2C speed - * * I2C autoreset on failure - * 0.4 2011-02-22 Olli Vanhoja * mfs::renameFile(char [FILENAME_LENGTH], char [FILENAME_LENGTH] function added - * * Incresed fault tolerance by first allocating new - * block and then linking to it from previous block - * * Reconstructed initialization and use of some variables - * 0.5 2011-02-22 Olli Vanhoja * Improved documentation - * * Block variables changed from char to uint32_t for - * code optimization (and for possible 16bit update which - * would technically allow 256 TB volume sizes) - * * Optimized file searching algorithms - * 0.6 2011-02-22 Olli Vanhoja * Fixed file remove issue - * * Fixed mkfs bug - * 0.7 2011-02-23 Olli Vanhoja * file::seek(uint32_t) added - * 0.7 2011-02-23 Olli Vanhoja * Fixed destroying prev link issue on flush - * * Fixed Forwarding issue which moved cursor at the begining - * of the filename block - * * Separated locating of next/prev links functionality - * into its own private function - * * 16 bit block number pointers 256 TB theoretical maximum - * volume size, WHOA \O/ - * * Remove and rename respects RO bit - * * SetFileFlags fixed - * * New file write mode DWRITE - * 0.8 2011-02-24 Olli Vanhoja * EOF should be now found in AWRITE mode and EOF is - * * also written by mfs::createFile() so there should - * be no more confusing situations with new files. - * - * TODO : - * * Directory support (VOL blocks?) - * * RAID 0 & 1 - *H*/ - -#ifndef MFS_H -#define MFS_H - -#include "i2c_eeprom.h" - -const unsigned int VOL_SIZE=65536; /**< EEPROM chip size in bytes */ -const unsigned int BS=256; /**< How many bytes per block (default: 256 bytes) */ -const unsigned int BC=VOL_SIZE / BS; // block count -const char mEOF='\x01'; // End Of File/Section marked -const unsigned int BUF=1050; /**< File buffer length */ -const unsigned int FILENAME_LENGTH=64; - -/** mFS File System class - * - * This class is used as a handle for the fs in use. - */ -class mfs -{ -private: - i2c_eeprom *mem; // Only 512 kB I2C EEPROM is supported ATM -public: - /** Create a new file system object - * - * @param xi2c_address a Physical I2C address of the EEPROM chip - */ - mfs(int i2c_address); - - /** Read data from specified fs block - * - * @param *data Pointer for readed data - * @param block Block number. - * @param byte Selected byte. - * @param n Bytes to be read. - * @returns Error code: 0 = OK, 1 = Incorrect input - */ - char read(char *data, uint32_t block, uint32_t byte, uint32_t n); - - /** Write data to specified fs block - * - * @param *data Pointer for readed data - * @param block Block number. - * @param byte Selected byte. - * @param n Bytes to be read. - * @returns Error code: 0 = OK, 1 = Incorrect input - */ - char write(char *data, uint32_t block, uint32_t byte, uint32_t n); - - /** Locate next free block - * - * @param *blockOut Returns next free block from begining of the fs. - * @returns Error code: 0 = OK, 1 = Out of space - */ - char getNextFreeBlock(uint32_t *blockOut); - - /** Locates next starting file from parameter block - * - * @param block Start scanning from this block. - * @param *filenameOut Return name of the file found. - * @param Returns block number of the file found. - * @returns Error code: 0 = OK, 1 = Empty fs - */ - char findNextFile(uint32_t block, char *filenameOut, uint32_t *blockOut); - - /** Get block number of the given file - * - * Returns block number of the block flaged with FBOF flag. - * - * @param filename[FILENAME_LENGTH] Filename input. - * @param Returns block number of the first block of the given file. - * @returns Error code: 0 = OK, 1 = File not found - */ - char getFirstBlockOfFile(char filename[FILENAME_LENGTH], uint32_t *blockOut); - - /** Create a new empty file - * - * Reserves one block for the file created. - * - * @param filename[FILENAME_LENGTH] Filename input. - * @returns Error code: 0 = OK, 1 = File exists already, 2 = Out of space - */ - char createFile(char filename[FILENAME_LENGTH]); - - /** Remove a file from the file system - * - * @param filename[FILENAME_LENGTH] Filename input. - * @returns Error code: 0 = OK, 1 = File doesn't exists, 2 = RO file - */ - char removeFile(char filename[FILENAME_LENGTH]); - - /** Rename a file - * - * @param oldFilename[FILENAME_LENGTH] Old filename. - * @param newFilename[FILENAME_LENGTH] New file name. - * @returns Error code: 0 = OK, 1 = File doesn't exists, 2 = RO file, 3 = fs is corrupted - */ - char renameFile(char oldFilename[FILENAME_LENGTH], char newFilename[FILENAME_LENGTH]); - - /** Set user modifiable flags. - * - * \code - * desc RO|HIDDEN|LOCK - * bit 3 2 1 - * \endcode - * - * @param *flags Flag input - * @param filename[FILENAME_LENGTH] Filename input. - * @returns Error code: 0 = OK, 1 = File doesn't exists, 2 = File system is corrupted - */ - char setFileFlags(char *flags, char filename[FILENAME_LENGTH]); - - /** Read user modifiable flags. - * - * \code - * desc RO|HIDDEN|LOCK - * bit 3 2 1 - * \endcode - * - * @param *flags Flag output - * @param filename[FILENAME_LENGTH] Filename input. - * @returns Error code: 0 = OK, 1 = File doesn't exists - */ - char getFileFlags(char *flags, char filename[FILENAME_LENGTH]); - - /** Get number of free blocks - * - * @returns Number of free blocks. - */ - uint32_t free(); - - /** Format new file system - * - * \note Keep in mind that only first byte is checked for functionality and - * if it's broken the who file system is useless. - * - * @param createLabel Create volume label at the begining of the file system. (there is no specified use for volume labels atm). - * @returns Number of bad block headers. - */ - uint32_t mkfs(bool createLabel); -}; - -enum BlockLinkType {NEXT, PREV}; -enum FileOpenMode {RO, AWRITE, DWRITE}; - -/** mFS File handle class - * - * This class provides a file handle and data manipulation methods to be - * used for files stored in mFS files system. - */ -class file -{ -private: - mfs *fs; // Reference to the file system in use - FileOpenMode fMode; - char buffer[BUF]; // Write buffer - uint32_t bufPos; // "Cursor" position in buffer - uint32_t firstBlock; // First block of the file - uint32_t currBlock; // Current block in use - uint32_t blockPos; // "head" position on the current block - uint32_t byteCount; // Stores current "cursor" position in file for seek - // Private functions - char getBlockLink(BlockLinkType linkSelection, uint32_t *blockOut); - char removeFollowingBlocks(uint32_t block); // Offers destructive write/very simple wear levelling -public: - /** Create file handle - * - * \warning File must be created before it can be opened! - * Opening non-existing file will trip the system to error(); - * If read only file is opened in rw mode system will trip to error(). - * - * \b AWRITE is a file access mode where cursor can be moved along the file - * and write can be started at any point. write() function will overwrite - * only as many bytes as you chosen to write. - * - * \b DWRITE is a file access mode similiar to AWRITE but when you start - * writing all the data after cursor will be removed permanently and flush() - * will set a new EOF marker. - * - * @param filename[FILENAME_LENGTH] Filename input. - * @param operation RO = Read only, AWRITE = read and write, DWRITE = read + destructive write. - */ - file(mfs *fs_ref, char filename[FILENAME_LENGTH], FileOpenMode operation); - - /** Close file handle - * - * Flushes the file and closes the handle. - */ - ~file(); // Close file handle and flush - - /** Rewind to the start postion of the file - * - */ - void rewind(); - - /** Reverse n bytes back - * - * @param n Number of bytes. - * @returns Error code: 0 = OK, 1 = First byte of file. - */ - char rewind(uint32_t n); - - /** Forward one byte - * - * @returns Error code: 0 = OK, 1 = End of file. - */ - char forward(); - - /** Forward n bytes - * - * @param n Number of blocks. - * @returns Error code: 0 = OK, 1 = End of file. - */ - char forward(uint32_t n); - - /** Seek to byte given - * - * @param byte Byte number where to seek. - * @returns Error code: 0 = OK, 1 = End of file or already at first byte. - */ - char seek(uint32_t byte); - - /** Reads a string of bytes - * - * Always places '\0' at the end of string. - * - * @param data Output buffer. - * @param n Number of bytes to be read. - * @returns Error code. 0 = OK, 1 = Last block of the file - */ - void read(char *data, uint32_t n); - - /** Reads a binary array of bytes - * - * Doesn't add '\0' at the end of data array and doesn't respect mEOF byte. - * - * @param data Output buffer. - * @param n Number of bytes to be read. - */ - void readBin(char *data, uint32_t n); - - /** Write byte array to a file (buffer) - * - * @param data Input data. - * @param n Number of bytes to be read. - * @returns Error code: 0 = OK, 1 = Flush failed. - */ - char write(char *data, uint32_t n); - - /** Flush file buffer - * Writes buffer to the EEPROM chip in use. - * - * @returns Error code: 0 = OK, 1 = Out of free space, 2 = Destructive operation failed (fs is corrupted). - */ - char flush(); -}; - -#endif
--- a/ICE-Application/src/add-ons/ICELog/ICELog.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -#include "mbed.h" -#include <stdarg.h> -#include "ICELog.h" - -//using namespace mts; - -int ICELog::currentLevel = ICELog::WARNING_LEVEL; - -const char* ICELog::NONE_LABEL = "NONE"; -const char* ICELog::FATAL_LABEL = "FATAL"; -const char* ICELog::ERROR_LABEL = "ERROR"; -const char* ICELog::WARNING_LABEL = "WARNING"; -const char* ICELog::INFO_LABEL = "INFO"; -const char* ICELog::DEBUG_LABEL = "DEBUG"; -const char* ICELog::TRACE_LABEL = "TRACE"; - -void ICELog::printMessage(int level, const char* format, ...) { - if (printable(level)) { - va_list argptr; - va_start(argptr, format); - vprintf(format, argptr); - va_end(argptr); - } -} - -bool ICELog::printable(int level) { - return level <= currentLevel; -} - -void ICELog::setLogLevel(int level) { - if (level < NONE_LEVEL) - currentLevel = NONE_LEVEL; - else if (level > TRACE_LEVEL) - currentLevel = TRACE_LEVEL; - else - currentLevel = level; -} - -int ICELog::getLogLevel() { - return currentLevel; -} - -const char* ICELog::getLogLevelString() { - switch (currentLevel) { - case NONE_LEVEL: - return NONE_LABEL; - case FATAL_LEVEL: - return FATAL_LABEL; - case ERROR_LEVEL: - return ERROR_LABEL; - case WARNING_LEVEL: - return WARNING_LABEL; - case INFO_LEVEL: - return INFO_LABEL; - case DEBUG_LEVEL: - return DEBUG_LABEL; - case TRACE_LEVEL: - return TRACE_LABEL; - default: - return "unknown"; - } -} - \ No newline at end of file
--- a/ICE-Application/src/add-ons/ICELog/ICELog.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -#ifndef ICELOG_H -#define ICELOG_H - -#include <string> - -inline const char* className(const std::string& prettyFunction) -{ - size_t colons = prettyFunction.find_last_of("::"); - if (colons == std::string::npos) - return ""; - size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1; - size_t end = colons - begin; - - return prettyFunction.substr(begin,end).c_str(); -} - -#define __CLASSNAME__ className(__PRETTY_FUNCTION__) - - -#ifdef MTS_DEBUG - -#define logFatal(format, ...) \ - ICELog::printMessage(ICELog::FATAL_LEVEL, "%s:%s:%d| [%s] " format "\r\n", __CLASSNAME__, __func__, __LINE__, ICELog::FATAL_LABEL, ##__VA_ARGS__) -#define logError(format, ...) \ - ICELog::printMessage(ICELog::ERROR_LEVEL, "%s:%s:%d| [%s] " format "\r\n", __CLASSNAME__, __func__, __LINE__, ICELog::ERROR_LABEL, ##__VA_ARGS__) -#define logWarning(format, ...) \ - ICELog::printMessage(ICELog::WARNING_LEVEL, "%s:%s:%d| [%s] " format "\r\n", __CLASSNAME__, __func__, __LINE__, ICELog::WARNING_LABEL, ##__VA_ARGS__) -#define logInfo(format, ...) \ - ICELog::printMessage(ICELog::INFO_LEVEL, "%s:%s:%d| [%s] " format "\r\n", __CLASSNAME__, __func__, __LINE__, ICELog::INFO_LABEL, ##__VA_ARGS__) -#define logDebug(format, ...) \ - ICELog::printMessage(ICELog::DEBUG_LEVEL, "%s:%s:%d| [%s] " format "\r\n", __CLASSNAME__, __func__, __LINE__, ICELog::DEBUG_LABEL, ##__VA_ARGS__) -#define logTrace(format, ...) \ - ICELog::printMessage(ICELog::TRACE_LEVEL, "%s:%s:%d| [%s] " format "\r\n", __CLASSNAME__, __func__, __LINE__, ICELog::TRACE_LABEL, ##__VA_ARGS__) - -#else - -#define logFatal(format, ...) \ - ICELog::printMessage(ICELog::FATAL_LEVEL, "[%s] " format "\r\n", ICELog::FATAL_LABEL, ##__VA_ARGS__) -#define logError(format, ...) \ - ICELog::printMessage(ICELog::ERROR_LEVEL, "[%s] " format "\r\n", ICELog::ERROR_LABEL, ##__VA_ARGS__) -#define logWarning(format, ...) \ - ICELog::printMessage(ICELog::WARNING_LEVEL, "[%s] " format "\r\n", ICELog::WARNING_LABEL, ##__VA_ARGS__) -#define logInfo(format, ...) \ - ICELog::printMessage(ICELog::INFO_LEVEL, "[%s] " format "\r\n", ICELog::INFO_LABEL, ##__VA_ARGS__) -#define logDebug(format, ...) \ - ICELog::printMessage(ICELog::DEBUG_LEVEL, "[%s] " format "\r\n", ICELog::DEBUG_LABEL, ##__VA_ARGS__) -#define logTrace(format, ...) \ - ICELog::printMessage(ICELog::TRACE_LEVEL, "[%s] " format "\r\n", ICELog::TRACE_LABEL, ##__VA_ARGS__) - -#endif // MTS_DEBUG - -class ICELog -{ -public: - - /** Enum of log levels. - */ - enum logLevel { - NONE_LEVEL = 0, - FATAL_LEVEL = 1, - ERROR_LEVEL = 2, - WARNING_LEVEL = 3, - INFO_LEVEL = 4, - DEBUG_LEVEL = 5, - TRACE_LEVEL = 6 - }; - - /** Print log message. - */ - static void printMessage(int level, const char* format, ...); - - /** Determine if the given level is currently printable. - */ - static bool printable(int level); - - /** Set log level - * Messages with lower priority than the current level will not be printed. - * If the level is set to NONE, no messages will print. - */ - static void setLogLevel(int level); - - /** Get the current log level. - */ - static int getLogLevel(); - - /** Get string representation of the current log level. - */ - static const char* getLogLevelString(); - - static const char* NONE_LABEL; - static const char* FATAL_LABEL; - static const char* ERROR_LABEL; - static const char* WARNING_LABEL; - static const char* INFO_LABEL; - static const char* DEBUG_LABEL; - static const char* TRACE_LABEL; - - ICELog() {} - -private: - static int currentLevel; - //ICELog(); -}; - -#endif
--- a/ICE-Application/src/add-ons/NaturalTinyShell.lib Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://developer.mbed.org/users/jmarkel44/code/NaturalTinyShell/#2cafac575f61
--- a/ICE-Application/src/add-ons/cJSON/cJSON.c Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,752 +0,0 @@ - -/* - Copyright (c) 2009 Dave Gamble - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -/* cJSON */ -/* JSON parser in C. */ - -#include <string.h> -#include <stdio.h> -#include <math.h> -#include <stdlib.h> -#include <float.h> -#include <limits.h> -#include <ctype.h> -#include "cJSON.h" - -static const char *global_ep; - -const char *cJSON_GetErrorPtr(void) {return global_ep;} - -static int cJSON_strcasecmp(const char *s1,const char *s2) -{ - if (!s1) return (s1==s2)?0:1;if (!s2) return 1; - for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); -} - -static void *(*cJSON_malloc)(size_t sz) = malloc; -static void (*cJSON_free)(void *ptr) = free; - -static char* cJSON_strdup(const char* str) -{ - size_t len; - char* copy; - - len = strlen(str) + 1; - if (!(copy = (char*)cJSON_malloc(len))) return 0; - memcpy(copy,str,len); - return copy; -} - -void cJSON_InitHooks(cJSON_Hooks* hooks) -{ - if (!hooks) { /* Reset hooks */ - cJSON_malloc = malloc; - cJSON_free = free; - return; - } - - cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; - cJSON_free = (hooks->free_fn)?hooks->free_fn:free; -} - -/* Internal constructor. */ -static cJSON *cJSON_New_Item(void) -{ - cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); - if (node) memset(node,0,sizeof(cJSON)); - return node; -} - -/* Delete a cJSON structure. */ -void cJSON_Delete(cJSON *c) -{ - cJSON *next; - while (c) - { - next=c->next; - if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); - if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); - if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string); - cJSON_free(c); - c=next; - } -} - -/* Parse the input text to generate a number, and populate the result into item. */ -static const char *parse_number(cJSON *item,const char *num) -{ - double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; - - if (*num=='-') sign=-1,num++; /* Has sign? */ - if (*num=='0') num++; /* is zero */ - if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ - if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ - if (*num=='e' || *num=='E') /* Exponent? */ - { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ - while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ - } - - n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ - - item->valuedouble=n; - item->valueint=(int)n; - item->type=cJSON_Number; - return num; -} - -static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; } - -typedef struct {char *buffer; int length; int offset; } printbuffer; - -static char* ensure(printbuffer *p,int needed) -{ - char *newbuffer;int newsize; - if (!p || !p->buffer) return 0; - needed+=p->offset; - if (needed<=p->length) return p->buffer+p->offset; - - newsize=pow2gt(needed); - newbuffer=(char*)cJSON_malloc(newsize); - if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;} - if (newbuffer) memcpy(newbuffer,p->buffer,p->length); - cJSON_free(p->buffer); - p->length=newsize; - p->buffer=newbuffer; - return newbuffer+p->offset; -} - -static int update(printbuffer *p) -{ - char *str; - if (!p || !p->buffer) return 0; - str=p->buffer+p->offset; - return p->offset+strlen(str); -} - -/* Render the number nicely from the given item into a string. */ -static char *print_number(cJSON *item,printbuffer *p) -{ - char *str=0; - double d=item->valuedouble; - if (d==0) - { - if (p) str=ensure(p,2); - else str=(char*)cJSON_malloc(2); /* special case for 0. */ - if (str) strcpy(str,"0"); - } - else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) - { - if (p) str=ensure(p,21); - else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ - if (str) sprintf(str,"%d",item->valueint); - } - else - { - if (p) str=ensure(p,64); - else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ - if (str) - { - if (d*0!=0) sprintf(str,"null"); /* This checks for NaN and Infinity */ - else if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60) sprintf(str,"%.0f",d); - else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); - else sprintf(str,"%f",d); - } - } - return str; -} - -static unsigned parse_hex4(const char *str) -{ - unsigned h=0; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - return h; -} - -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; -static const char *parse_string(cJSON *item,const char *str,const char **ep) -{ - const char *ptr=str+1,*end_ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; - if (*str!='\"') {*ep=str;return 0;} /* not a string! */ - - while (*end_ptr!='\"' && *end_ptr && ++len) if (*end_ptr++ == '\\') end_ptr++; /* Skip escaped quotes. */ - - out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ - if (!out) return 0; - item->valuestring=out; /* assign here so out will be deleted during cJSON_Delete() later */ - item->type=cJSON_String; - - ptr=str+1;ptr2=out; - while (ptr < end_ptr) - { - if (*ptr!='\\') *ptr2++=*ptr++; - else - { - ptr++; - switch (*ptr) - { - case 'b': *ptr2++='\b'; break; - case 'f': *ptr2++='\f'; break; - case 'n': *ptr2++='\n'; break; - case 'r': *ptr2++='\r'; break; - case 't': *ptr2++='\t'; break; - case 'u': /* transcode utf16 to utf8. */ - uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ - if (ptr >= end_ptr) {*ep=str;return 0;} /* invalid */ - - if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) {*ep=str;return 0;} /* check for invalid. */ - - if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ - { - if (ptr+6 > end_ptr) {*ep=str;return 0;} /* invalid */ - if (ptr[1]!='\\' || ptr[2]!='u') {*ep=str;return 0;} /* missing second-half of surrogate. */ - uc2=parse_hex4(ptr+3);ptr+=6; - if (uc2<0xDC00 || uc2>0xDFFF) {*ep=str;return 0;} /* invalid second-half of surrogate. */ - uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); - } - - len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; - - switch (len) { - case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 1: *--ptr2 =(uc | firstByteMark[len]); - } - ptr2+=len; - break; - default: *ptr2++=*ptr; break; - } - ptr++; - } - } - *ptr2=0; - if (*ptr=='\"') ptr++; - return ptr; -} - -/* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const char *str,printbuffer *p) -{ - const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token; - - if (!str) - { - if (p) out=ensure(p,3); - else out=(char*)cJSON_malloc(3); - if (!out) return 0; - strcpy(out,"\"\""); - return out; - } - - for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0; - if (!flag) - { - len=ptr-str; - if (p) out=ensure(p,len+3); - else out=(char*)cJSON_malloc(len+3); - if (!out) return 0; - ptr2=out;*ptr2++='\"'; - strcpy(ptr2,str); - ptr2[len]='\"'; - ptr2[len+1]=0; - return out; - } - - ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} - - if (p) out=ensure(p,len+3); - else out=(char*)cJSON_malloc(len+3); - if (!out) return 0; - - ptr2=out;ptr=str; - *ptr2++='\"'; - while (*ptr) - { - if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; - else - { - *ptr2++='\\'; - switch (token=*ptr++) - { - case '\\': *ptr2++='\\'; break; - case '\"': *ptr2++='\"'; break; - case '\b': *ptr2++='b'; break; - case '\f': *ptr2++='f'; break; - case '\n': *ptr2++='n'; break; - case '\r': *ptr2++='r'; break; - case '\t': *ptr2++='t'; break; - default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ - } - } - } - *ptr2++='\"';*ptr2++=0; - return out; -} -/* Invote print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);} - -/* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item,const char *value,const char **ep); -static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p); -static const char *parse_array(cJSON *item,const char *value,const char **ep); -static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p); -static const char *parse_object(cJSON *item,const char *value,const char **ep); -static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p); - -/* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} - -/* Parse an object - create a new root, and populate. */ -cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) -{ - const char *end=0,**ep=return_parse_end?return_parse_end:&global_ep; - cJSON *c=cJSON_New_Item(); - *ep=0; - if (!c) return 0; /* memory fail */ - - end=parse_value(c,skip(value),ep); - if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ - - /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ - if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);*ep=end;return 0;}} - if (return_parse_end) *return_parse_end=end; - return c; -} -/* Default options for cJSON_Parse */ -cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} - -/* Render a cJSON item/entity/structure to text. */ -char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);} -char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);} - -char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt) -{ - printbuffer p; - p.buffer=(char*)cJSON_malloc(prebuffer); - p.length=prebuffer; - p.offset=0; - return print_value(item,0,fmt,&p); -} - - -/* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item,const char *value,const char **ep) -{ - if (!value) return 0; /* Fail on null. */ - if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } - if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } - if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } - if (*value=='\"') { return parse_string(item,value,ep); } - if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } - if (*value=='[') { return parse_array(item,value,ep); } - if (*value=='{') { return parse_object(item,value,ep); } - - *ep=value;return 0; /* failure. */ -} - -/* Render a value to text. */ -static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p) -{ - char *out=0; - if (!item) return 0; - if (p) - { - switch ((item->type)&255) - { - case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;} - case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;} - case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;} - case cJSON_Number: out=print_number(item,p);break; - case cJSON_String: out=print_string(item,p);break; - case cJSON_Array: out=print_array(item,depth,fmt,p);break; - case cJSON_Object: out=print_object(item,depth,fmt,p);break; - } - } - else - { - switch ((item->type)&255) - { - case cJSON_NULL: out=cJSON_strdup("null"); break; - case cJSON_False: out=cJSON_strdup("false");break; - case cJSON_True: out=cJSON_strdup("true"); break; - case cJSON_Number: out=print_number(item,0);break; - case cJSON_String: out=print_string(item,0);break; - case cJSON_Array: out=print_array(item,depth,fmt,0);break; - case cJSON_Object: out=print_object(item,depth,fmt,0);break; - } - } - return out; -} - -/* Build an array from input text. */ -static const char *parse_array(cJSON *item,const char *value,const char **ep) -{ - cJSON *child; - if (*value!='[') {*ep=value;return 0;} /* not an array! */ - - item->type=cJSON_Array; - value=skip(value+1); - if (*value==']') return value+1; /* empty array. */ - - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; /* memory fail */ - value=skip(parse_value(child,skip(value),ep)); /* skip any spacing, get the value. */ - if (!value) return 0; - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_value(child,skip(value+1),ep)); - if (!value) return 0; /* memory fail */ - } - - if (*value==']') return value+1; /* end of array */ - *ep=value;return 0; /* malformed. */ -} - -/* Render an array to text */ -static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p) -{ - char **entries; - char *out=0,*ptr,*ret;int len=5; - cJSON *child=item->child; - int numentries=0,i=0,fail=0; - size_t tmplen=0; - - /* How many entries in the array? */ - while (child) numentries++,child=child->next; - /* Explicitly handle numentries==0 */ - if (!numentries) - { - if (p) out=ensure(p,3); - else out=(char*)cJSON_malloc(3); - if (out) strcpy(out,"[]"); - return out; - } - - if (p) - { - /* Compose the output array. */ - i=p->offset; - ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++; - child=item->child; - while (child && !fail) - { - print_value(child,depth+1,fmt,p); - p->offset=update(p); - if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;} - child=child->next; - } - ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0; - out=(p->buffer)+i; - } - else - { - /* Allocate an array to hold the values for each */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - memset(entries,0,numentries*sizeof(char*)); - /* Retrieve all the results: */ - child=item->child; - while (child && !fail) - { - ret=print_value(child,depth+1,fmt,0); - entries[i++]=ret; - if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; - child=child->next; - } - - /* If we didn't fail, try to malloc the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - /* If that fails, we fail. */ - if (!out) fail=1; - - /* Handle failure. */ - if (fail) - { - for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]); - cJSON_free(entries); - return 0; - } - - /* Compose the output array. */ - *out='['; - ptr=out+1;*ptr=0; - for (i=0;i<numentries;i++) - { - tmplen=strlen(entries[i]);memcpy(ptr,entries[i],tmplen);ptr+=tmplen; - if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;} - cJSON_free(entries[i]); - } - cJSON_free(entries); - *ptr++=']';*ptr++=0; - } - return out; -} - -/* Build an object from the text. */ -static const char *parse_object(cJSON *item,const char *value,const char **ep) -{ - cJSON *child; - if (*value!='{') {*ep=value;return 0;} /* not an object! */ - - item->type=cJSON_Object; - value=skip(value+1); - if (*value=='}') return value+1; /* empty array. */ - - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; - value=skip(parse_string(child,skip(value),ep)); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {*ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1),ep)); /* skip any spacing, get the value. */ - if (!value) return 0; - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_string(child,skip(value+1),ep)); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {*ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1),ep)); /* skip any spacing, get the value. */ - if (!value) return 0; - } - - if (*value=='}') return value+1; /* end of array */ - *ep=value;return 0; /* malformed. */ -} - -/* Render an object to text. */ -static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p) -{ - char **entries=0,**names=0; - char *out=0,*ptr,*ret,*str;int len=7,i=0,j; - cJSON *child=item->child; - int numentries=0,fail=0; - size_t tmplen=0; - /* Count the number of entries. */ - while (child) numentries++,child=child->next; - /* Explicitly handle empty object case */ - if (!numentries) - { - if (p) out=ensure(p,fmt?depth+4:3); - else out=(char*)cJSON_malloc(fmt?depth+4:3); - if (!out) return 0; - ptr=out;*ptr++='{'; - if (fmt) {*ptr++='\n';for (i=0;i<depth;i++) *ptr++='\t';} - *ptr++='}';*ptr++=0; - return out; - } - if (p) - { - /* Compose the output: */ - i=p->offset; - len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0; - *ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len; - child=item->child;depth++; - while (child) - { - if (fmt) - { - ptr=ensure(p,depth); if (!ptr) return 0; - for (j=0;j<depth;j++) *ptr++='\t'; - p->offset+=depth; - } - print_string_ptr(child->string,p); - p->offset=update(p); - - len=fmt?2:1; - ptr=ensure(p,len); if (!ptr) return 0; - *ptr++=':';if (fmt) *ptr++='\t'; - p->offset+=len; - - print_value(child,depth,fmt,p); - p->offset=update(p); - - len=(fmt?1:0)+(child->next?1:0); - ptr=ensure(p,len+1); if (!ptr) return 0; - if (child->next) *ptr++=','; - if (fmt) *ptr++='\n';*ptr=0; - p->offset+=len; - child=child->next; - } - ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0; - if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t'; - *ptr++='}';*ptr=0; - out=(p->buffer)+i; - } - else - { - /* Allocate space for the names and the objects */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - names=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!names) {cJSON_free(entries);return 0;} - memset(entries,0,sizeof(char*)*numentries); - memset(names,0,sizeof(char*)*numentries); - - /* Collect all the results into our arrays: */ - child=item->child;depth++;if (fmt) len+=depth; - while (child && !fail) - { - names[i]=str=print_string_ptr(child->string,0); - entries[i++]=ret=print_value(child,depth,fmt,0); - if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; - child=child->next; - } - - /* Try to allocate the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - if (!out) fail=1; - - /* Handle failure */ - if (fail) - { - for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);} - cJSON_free(names);cJSON_free(entries); - return 0; - } - - /* Compose the output: */ - *out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0; - for (i=0;i<numentries;i++) - { - if (fmt) for (j=0;j<depth;j++) *ptr++='\t'; - tmplen=strlen(names[i]);memcpy(ptr,names[i],tmplen);ptr+=tmplen; - *ptr++=':';if (fmt) *ptr++='\t'; - strcpy(ptr,entries[i]);ptr+=strlen(entries[i]); - if (i!=numentries-1) *ptr++=','; - if (fmt) *ptr++='\n';*ptr=0; - cJSON_free(names[i]);cJSON_free(entries[i]); - } - - cJSON_free(names);cJSON_free(entries); - if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t'; - *ptr++='}';*ptr++=0; - } - return out; -} - -/* Get Array size/item / object item. */ -int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;} -cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array?array->child:0;while (c && item>0) item--,c=c->next; return c;} -cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object?object->child:0;while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} -int cJSON_HasObjectItem(cJSON *object,const char *string) {return cJSON_GetObjectItem(object,string)?1:0;} - -/* Utility for array list handling. */ -static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} -/* Utility for handling references. */ -static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} - -/* Add item to array/object. */ -void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} -void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} -void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);} -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} -void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} - -cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; - if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} -void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} -cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} -void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} - -/* Replace array/object items with new ones. */ -void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;} - newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;} -void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; - newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; - if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} -void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} - -/* Create basic types: */ -cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} -cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} -cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} -cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} -cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} -cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);if(!item->valuestring){cJSON_Delete(item);return 0;}}return item;} -cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} -cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} - -/* Create Arrays: */ -cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!n){cJSON_Delete(a);return 0;}if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!n){cJSON_Delete(a);return 0;}if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!n){cJSON_Delete(a);return 0;}if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!n){cJSON_Delete(a);return 0;}if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;} - -/* Duplication */ -cJSON *cJSON_Duplicate(cJSON *item,int recurse) -{ - cJSON *newitem,*cptr,*nptr=0,*newchild; - /* Bail on bad ptr */ - if (!item) return 0; - /* Create new item */ - newitem=cJSON_New_Item(); - if (!newitem) return 0; - /* Copy over all vars */ - newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; - if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} - if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} - /* If non-recursive, then we're done! */ - if (!recurse) return newitem; - /* Walk the ->next chain for the child. */ - cptr=item->child; - while (cptr) - { - newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) {cJSON_Delete(newitem);return 0;} - if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ - cptr=cptr->next; - } - return newitem; -} - -void cJSON_Minify(char *json) -{ - char *into=json; - while (*json) - { - if (*json==' ') json++; - else if (*json=='\t') json++; /* Whitespace characters. */ - else if (*json=='\r') json++; - else if (*json=='\n') json++; - else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */ - else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */ - else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */ - else *into++=*json++; /* All other characters. */ - } - *into=0; /* and null-terminate. */ -} \ No newline at end of file
--- a/ICE-Application/src/add-ons/cJSON/cJSON.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,153 +0,0 @@ -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#ifndef cJSON__h -#define cJSON__h - -#ifdef __cplusplus -extern "C" -{ -#endif - -/* cJSON Types: */ -#define cJSON_False (1 << 0) -#define cJSON_True (1 << 1) -#define cJSON_NULL (1 << 2) -#define cJSON_Number (1 << 3) -#define cJSON_String (1 << 4) -#define cJSON_Array (1 << 5) -#define cJSON_Object (1 << 6) - -#define cJSON_IsReference 256 -#define cJSON_StringIsConst 512 - -/* The cJSON structure: */ -typedef struct cJSON { - struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ - - int type; /* The type of the item, as above. */ - - char *valuestring; /* The item's string, if type==cJSON_String */ - int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; /* The item's number, if type==cJSON_Number */ - - char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ -} cJSON; - -typedef struct cJSON_Hooks { - void *(*malloc_fn)(size_t sz); - void (*free_fn)(void *ptr); -} cJSON_Hooks; - -/* Supply malloc, realloc and free functions to cJSON */ -extern void cJSON_InitHooks(cJSON_Hooks* hooks); - - -/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ -extern cJSON *cJSON_Parse(const char *value); -/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ -extern char *cJSON_Print(cJSON *item); -/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ -extern char *cJSON_PrintUnformatted(cJSON *item); -/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ -extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt); -/* Delete a cJSON entity and all subentities. */ -extern void cJSON_Delete(cJSON *c); - -/* Returns the number of items in an array (or object). */ -extern int cJSON_GetArraySize(cJSON *array); -/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ -extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); -/* Get item "string" from object. Case insensitive. */ -extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); -extern int cJSON_HasObjectItem(cJSON *object,const char *string); -/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ -extern const char *cJSON_GetErrorPtr(void); - -/* These calls create a cJSON item of the appropriate type. */ -extern cJSON *cJSON_CreateNull(void); -extern cJSON *cJSON_CreateTrue(void); -extern cJSON *cJSON_CreateFalse(void); -extern cJSON *cJSON_CreateBool(int b); -extern cJSON *cJSON_CreateNumber(double num); -extern cJSON *cJSON_CreateString(const char *string); -extern cJSON *cJSON_CreateArray(void); -extern cJSON *cJSON_CreateObject(void); - -/* These utilities create an Array of count items. */ -extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); -extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); -extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); -extern cJSON *cJSON_CreateStringArray(const char **strings,int count); - -/* Append item to the specified array/object. */ -extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); -extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */ -/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ -extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); - -/* Remove/Detatch items from Arrays/Objects. */ -extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); -extern void cJSON_DeleteItemFromArray(cJSON *array,int which); -extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); -extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); - -/* Update array items. */ -extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */ -extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); -extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); - -/* Duplicate a cJSON item */ -extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); -/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will -need to be released. With recurse!=0, it will duplicate any children connected to the item. -The item->next and ->prev pointers are always zero on return from Duplicate. */ - -/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ -/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */ -extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); - -extern void cJSON_Minify(char *json); - -/* Macros for creating things quickly. */ -#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) -#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) -#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) -#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) -#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) -#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) - -/* When assigning an integer value, it needs to be propagated to valuedouble too. */ -#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) -#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) - -/* Macro for iterating over an array */ -#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next) - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file
--- a/ICE-Application/src/add-ons/crc32/crc.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -#include "mbed.h" -#include "crc.h" - -static const unsigned int crc32tab[256] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; - - -unsigned int crc32(char *p, int len) -{ - unsigned int crcinit = 0; - unsigned int crc = 0; - - crc = crcinit ^ 0xFFFFFFFF; - for (; len--; p++) { - crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[(crc ^ (*p)) & 0xFF]; - } - return crc ^ 0xFFFFFFFF; -}
--- a/ICE-Application/src/add-ons/crc32/crc.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -#ifndef CRC_H -#define CRC_H - -#include "mbed.h" - -unsigned int crc32(char *p, int len); - -#endif
--- a/ICE-Application/src/add-ons/v7/v7.c Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34484 +0,0 @@ -#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 <http://www.gnu.org/licenses/>. - * - * 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 <https://www.cesanta.com/license>. - */ - -#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_ESP_LWIP 3 -#define CS_P_CC3200 4 -#define CS_P_MSP432 5 -#define CS_P_CC3100 6 -#define CS_P_MBED 7 - -/* 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) -#define CS_PLATFORM CS_P_CC3200 -#elif defined(__unix__) || defined(__APPLE__) -#define CS_PLATFORM CS_P_UNIX -#elif defined(_WIN32) -#define CS_PLATFORM CS_P_WINDOWS -#elif defined(__MBED__) -#define CS_PLATFORM CS_P_MBED -#warning "CS_PLATFORM_CS_P_MBED" -#endif - -#ifndef CS_PLATFORM -#error "CS_PLATFORM is not specified and we couldn't guess it." -#endif - -#endif /* !defined(CS_PLATFORM) */ - -/* Amalgamated: #include "common/platforms/platform_unix.h" */ -/* Amalgamated: #include "common/platforms/platform_windows.h" */ -/* Amalgamated: #include "common/platforms/platform_esp_lwip.h" */ -/* Amalgamated: #include "common/platforms/platform_cc3200.h" */ -/* Amalgamated: #include "common/platforms/platform_cc3100.h" */ -/* Amalgamated: #include "common/platforms/platform_mbed.h" */ - -/* Common stuff */ - -#ifdef __GNUC__ -#define NORETURN __attribute__((noreturn)) -#define NOINLINE __attribute__((noinline)) -#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#define NOINSTR __attribute__((no_instrument_function)) -#else -#define NORETURN -#define NOINLINE -#define WARN_UNUSED_RESULT -#define NOINSTR -#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 - -#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 -#define _CRT_SECURE_NO_WARNINGS - -#include <assert.h> -#include <direct.h> -#include <errno.h> -#include <fcntl.h> -#include <io.h> -#include <limits.h> -#include <signal.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <time.h> - -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ -#endif - -#include <winsock2.h> -#include <ws2tcpip.h> -#include <windows.h> -#include <process.h> - -#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 fileno _fileno -#define vsnprintf _vsnprintf -#define sleep(x) Sleep((x) *1000) -#define to64(x) _atoi64(x) -#if !defined(__MINGW32__) && !defined(__MINGW64__) -#define popen(x, y) _popen((x), (y)) -#define pclose(x) _pclose(x) -#endif -#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 -#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 <stdint.h> -#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" -#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) -typedef struct stat cs_stat_t; -#else -typedef struct _stati64 cs_stat_t; -#endif -#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 '\\' - -#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 - -#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 - -/* <inttypes.h> 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 <arpa/inet.h> -#include <assert.h> -#include <ctype.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <stdint.h> -#include <limits.h> -#include <math.h> -#include <netdb.h> -#include <netinet/in.h> -#include <pthread.h> -#include <signal.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/select.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/types.h> -#include <unistd.h> - -/* - * osx correctly avoids defining strtoll when compiling in strict ansi mode. - * We require strtoll, and if your embedded pre-c99 compiler lacks one, please - * implement a shim. - */ -#if !(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 - -#endif /* CS_PLATFORM == CS_P_UNIX */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ */ -#ifdef V7_MODULE_LINES -#line 1 "common/platforms/platform_esp_lwip.h" -#endif -#ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP_LWIP_H_ -#define CS_COMMON_PLATFORMS_PLATFORM_ESP_LWIP_H_ -#if CS_PLATFORM == CS_P_ESP_LWIP - -#include <assert.h> -#include <ctype.h> -#include <fcntl.h> -#include <inttypes.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/time.h> - -#include <lwip/err.h> -#include <lwip/ip_addr.h> -#include <lwip/inet.h> -#include <lwip/netdb.h> -#include <lwip/dns.h> - -#ifndef LWIP_PROVIDE_ERRNO -#include <errno.h> -#endif - -#define LWIP_TIMEVAL_PRIVATE 0 - -#if LWIP_SOCKET -#include <lwip/sockets.h> -#define SOMAXCONN 10 -#else -/* We really need the definitions from sockets.h. */ -#undef LWIP_SOCKET -#define LWIP_SOCKET 1 -#include <lwip/sockets.h> -#undef LWIP_SOCKET -#define LWIP_SOCKET 0 -#endif - -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 - -unsigned long os_random(void); -#define random os_random - -#endif /* CS_PLATFORM == CS_P_ESP_LWIP */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP_LWIP_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_ - -#if defined(__cplusplus) -extern "C" { -#endif - -#include <stdlib.h> - -#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/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 simplelink.h is already included, all bets are off. */ -#if defined(MG_SOCKET_SIMPLELINK) && !defined(__SIMPLELINK_H__) - -#include <stdbool.h> - -#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 - -/* We want to disable SL_INC_STD_BSD_API_NAMING, so we include user.h ourselves - * and undef it. */ -#define PROVISIONING_API_H_ -#include <simplelink/user.h> -#undef PROVISIONING_API_H_ -#undef SL_INC_STD_BSD_API_NAMING - -#include <simplelink/include/simplelink.h> -#include <simplelink/include/netapp.h> - -/* 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(MG_SOCKET_SIMPLELINK) && !defined(__SIMPLELINK_H__) */ - -#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 <assert.h> -#include <ctype.h> -#include <errno.h> -#include <inttypes.h> -#include <stdint.h> -#include <string.h> -#include <time.h> - -#ifndef __TI_COMPILER_VERSION__ -#include <fcntl.h> -#include <sys/time.h> -#endif - -#define MG_SOCKET_SIMPLELINK 1 -#define MG_DISABLE_SOCKETPAIR 1 -#define MG_DISABLE_SYNC_RESOLVER 1 -#define MG_DISABLE_POPEN 1 -#define MG_DISABLE_CGI 1 -/* Only SPIFFS supports directories, SLFS does not. */ -#ifndef CC3200_FS_SPIFFS -#define MG_DISABLE_DAV 1 -#define MG_DISABLE_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 asprintf(char **strp, const char *fmt, ...); - -#endif - -/* TI's libc does not have stat & friends, add them. */ -#ifdef __TI_COMPILER_VERSION__ - -#include <file.h> - -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); -#define stat(a, b) _stat(a, b) - -#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) - -/* As of 5.2.7, TI compiler does not support va_copy() yet. */ -#define va_copy(apc, ap) ((apc) = (ap)) - -#endif /* __TI_COMPILER_VERSION__ */ - -#ifdef CC3200_FS_SPIFFS -#include <common/spiffs/spiffs.h> - -typedef struct { - spiffs_DIR dh; - struct spiffs_dirent de; -} DIR; - -#define d_name name -#define dirent spiffs_dirent - -DIR *opendir(const char *dir_name); -int closedir(DIR *dir); -struct dirent *readdir(DIR *dir); -#endif /* CC3200_FS_SPIFFS */ - -#ifdef CC3200_FS_SLFS -#define MG_FS_SLFS -#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_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 <assert.h> -#include <ctype.h> -#include <errno.h> -#include <inttypes.h> -#include <stdint.h> -#include <string.h> -#include <time.h> - -#define MG_SOCKET_SIMPLELINK 1 -#define MG_DISABLE_SOCKETPAIR 1 -#define MG_DISABLE_SYNC_RESOLVER 1 -#define MG_DISABLE_POPEN 1 -#define MG_DISABLE_CGI 1 -#define MG_DISABLE_DAV 1 -#define MG_DISABLE_DIRECTORY_LISTING 1 -#define MG_DISABLE_FILESYSTEM 1 - -/* - * 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 <simplelink.h> -#include <netapp.h> -#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/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 - -/* Amalgamated: #include "mbed.h" */ - -#endif /* CS_PLATFORM == CS_P_MBED */ -#endif /* CS_COMMON_PLATFORMS_PLATFORM_MBED_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 <stdarg.h> -#include <stdlib.h> - -#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); - -#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 - -#include <stdio.h> - -#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); - -#ifdef __cplusplus -} -#endif - -#endif /* DISABLE_BASE64 */ - -#endif /* CS_COMMON_BASE64_H_ */ -#ifdef V7_MODULE_LINES -#line 1 "common/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" */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -typedef struct MD5Context { - uint32_t buf[4]; - uint32_t bits[2]; - unsigned char in[64]; -} MD5_CTX; - -void MD5_Init(MD5_CTX *c); -void MD5_Update(MD5_CTX *c, const unsigned char *data, size_t len); -void MD5_Final(unsigned char *md, MD5_CTX *c); - -/* - * Return stringified MD5 hash for NULL terminated list of pointer/length pairs. - * A length should be specified as size_t variable. - * Example: - * - * char buf[33]; - * cs_md5(buf, "foo", (size_t) 3, "bar", (size_t) 3, NULL); - */ -char *cs_md5(char buf[33], ...); - -/* - * 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); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_COMMON_MD5_H_ */ -#ifdef V7_MODULE_LINES -#line 1 "common/sha1.h" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_COMMON_SHA1_H_ -#define CS_COMMON_SHA1_H_ - -#ifndef 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 /* 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_ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifdef CS_ENABLE_SPIFFS - -#include <spiffs.h> - -typedef struct { - spiffs_DIR dh; - struct spiffs_dirent de; -} DIR; - -#define d_name name -#define dirent spiffs_dirent - -int rmdir(const char *path); -int mkdir(const char *path, mode_t mode); - -#endif - -#if defined(_WIN32) -struct dirent { - char d_name[MAX_PATH]; -}; - -typedef struct DIR { - HANDLE handle; - WIN32_FIND_DATAW info; - struct dirent result; -} DIR; -#endif - -#if defined(_WIN32) || defined(CS_ENABLE_SPIFFS) -DIR *opendir(const char *dir_name); -int closedir(DIR *dir); -struct dirent *readdir(DIR *dir); -#endif - -#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" */ - -/* 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); - -#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 -#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/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" */ - -#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" */ - -/* Check whether we're compiling in an environment with no filesystem */ -#if defined(ARDUINO) && (ARDUINO == 106) -#define V7_NO_FS -#endif - -#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 <assert.h> -#ifndef NO_LIBC -#include <ctype.h> -#endif -#include <errno.h> -#include <float.h> -#include <limits.h> -#include <math.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <setjmp.h> - -/* 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 <stdint.h> -#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 defined(V7_ENABLE_GC_CHECK) || defined(V7_STACK_GUARD_MIN_SIZE) || \ - defined(V7_ENABLE_STACK_TRACKING) || defined(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 <stddef.h> /* For size_t */ -#include <stdio.h> /* 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 <inttypes.h> -#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 defined(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_RSHIFT, /* ( 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 -- a<b ) */ - OP_LE, /* ( a b -- a<=b ) */ - OP_GT, /* ( a b -- a>b ) */ - 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 varialble 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 defined(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 defined(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. - */ -#ifndef 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 defined(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 defined(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. - * - * 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); - -/* - * 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_ - -double cs_strtod(const char *str, char **endptr); - -#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 <stdio.h> -/* 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 { -#ifndef 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); - -#ifndef 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; - -#ifndef 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; - -#ifndef 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); - -#ifndef 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); - -#ifndef 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 *); - -#ifndef 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); - -#ifndef 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 !defined(V7_DISABLE_FILENAMES) && !defined(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 defined(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 <http://www.gnu.org/licenses/>. - * - * 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 <https://www.cesanta.com/license>. - */ - -#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 defined(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 <assert.h> -#include <string.h> -/* 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) { - mbuf->len = mbuf->size = 0; - mbuf->buf = NULL; - mbuf_resize(mbuf, initial_size); -} - -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) { - 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) { - mbuf_resize(mbuf, mbuf->len); -} - -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) { - return mbuf_insert(a, a->len, buf, len); -} - -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/platform.h" */ -/* Amalgamated: #include "common/str_util.h" */ - -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 - -#ifdef C_DISABLE_BUILTIN_SNPRINTF -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) { - 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 - <space> + ') - * 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, ...) { - 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) { - 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; -} - -#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 <stdarg.h> -#include <string.h> -/* Amalgamated: #include "common/platform.h" */ -/* Amalgamated: #include "common/utf.h" */ - -#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 <stdarg.h> -#include <string.h> -/* 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, /* ï½-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, /* Ç… Ç„ */ - 0x01c6, 498, /* dž Ç„ */ - 0x01c8, 499, /* Lj LJ */ - 0x01c9, 498, /* lj LJ */ - 0x01cb, 499, /* Ç‹ ÇŠ */ - 0x01cc, 498, /* ÇŒ ÇŠ */ - 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 ï½-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ž */ - 0x01c5, 501, /* Ç… dž */ - 0x01c7, 502, /* LJ lj */ - 0x01c8, 501, /* Lj lj */ - 0x01ca, 502, /* ÇŠ ÇŒ */ - 0x01cb, 501, /* Ç‹ ÇŒ */ - 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 <string.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 - -#ifndef CS_DISABLE_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_DISABLE_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) { - unsigned char a, b, c, d; - int orig_len = len; - 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; - return orig_len - len; -} - -#endif /* EXCLUDE_COMMON */ -#ifdef V7_MODULE_LINES -#line 1 "common/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. - */ - -#if !defined(DISABLE_MD5) && !defined(EXCLUDE_COMMON) - -/* Amalgamated: #include "common/md5.h" */ - -#ifndef CS_ENABLE_NATIVE_MD5 -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 MD5_Init(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 MD5Transform(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 MD5_Update(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); - MD5Transform(ctx->buf, (uint32_t *) ctx->in); - buf += t; - len -= t; - } - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32_t *) ctx->in); - buf += 64; - len -= 64; - } - - memcpy(ctx->in, buf, len); -} - -void MD5_Final(unsigned char digest[16], 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); - MD5Transform(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]; - - MD5Transform(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_ENABLE_NATIVE_MD5 */ - -/* - * 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) { - static const char *hex = "0123456789abcdef"; - - for (; len--; p++) { - *to++ = hex[p[0] >> 4]; - *to++ = hex[p[0] & 0x0f]; - } - *to = '\0'; -} - -char *cs_md5(char buf[33], ...) { - unsigned char hash[16]; - const unsigned char *p; - va_list ap; - MD5_CTX ctx; - - MD5_Init(&ctx); - - va_start(ap, buf); - while ((p = va_arg(ap, const unsigned char *) ) != NULL) { - size_t len = va_arg(ap, size_t); - MD5_Update(&ctx, p, len); - } - va_end(ap); - - MD5_Final(hash, &ctx); - cs_to_hex(buf, hash, sizeof(hash)); - - return buf; -} - -#endif /* EXCLUDE_COMMON */ -#ifdef V7_MODULE_LINES -#line 1 "common/sha1.c" -#endif -/* Copyright(c) By Steve Reid <steve@edmweb.com> */ -/* 100% Public Domain */ - -#if !defined(DISABLE_SHA1) && !defined(EXCLUDE_COMMON) - -/* Amalgamated: #include "common/sha1.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/cs_dirent.h" */ - -/* - * This file contains POSIX opendir/closedir/readdir API implementation - * for systems which do not natively support it (e.g. Windows). - */ - -#ifndef MG_FREE -#define MG_FREE free -#endif - -#ifndef MG_MALLOC -#define MG_MALLOC malloc -#endif - -#ifdef _WIN32 -DIR *opendir(const char *name) { - DIR *dir = NULL; - wchar_t wpath[MAX_PATH]; - DWORD attrs; - - if (name == NULL) { - SetLastError(ERROR_BAD_ARGUMENTS); - } else if ((dir = (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; -} - -int closedir(DIR *dir) { - 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 *dir) { - struct dirent *result = NULL; - - if (dir) { - 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 - -#ifdef CS_ENABLE_SPIFFS - -DIR *opendir(const char *dir_name) { - DIR *dir = NULL; - extern spiffs fs; - - if (dir_name != NULL && (dir = (DIR *) malloc(sizeof(*dir))) != NULL && - SPIFFS_opendir(&fs, (char *) dir_name, &dir->dh) == NULL) { - free(dir); - dir = NULL; - } - - return dir; -} - -int closedir(DIR *dir) { - if (dir != NULL) { - SPIFFS_closedir(&dir->dh); - free(dir); - } - return 0; -} - -struct dirent *readdir(DIR *dir) { - return SPIFFS_readdir(&dir->dh, &dir->de); -} - -/* SPIFFs doesn't support directory operations */ -int rmdir(const char *path) { - (void) path; - return ENOTDIR; -} - -int mkdir(const char *path, mode_t mode) { - (void) path; - (void) mode; - /* for spiffs supports only root dir, which comes from mongoose as '.' */ - return (strlen(path) == 1 && *path == '.') ? 0 : ENOTDIR; -} - -#endif /* CS_ENABLE_SPIFFS */ - -#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 <stdio.h> -#include <stdlib.h> - -#ifdef CS_MMAP -#include <fcntl.h> -#include <sys/mman.h> -#include <sys/stat.h> -#endif - -#ifndef EXCLUDE_COMMON -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) { - char *r; - int fd = open(path, O_RDONLY); - struct stat st; - if (fd == -1) 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_strtod.c" -#endif -#include <ctype.h> -#include <math.h> - -#include <stdlib.h> - -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 <string.h> -#include <stdlib.h> - -/* 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 - */ - -#if CS_PLATFORM == CS_P_MBED - -long timezone; - -#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 defined(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" */ - -#ifdef 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 <stdlib.h> -#include <string.h> - -/* 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/base64.h" */ - -#ifdef 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]) { - MD5_CTX ctx; - MD5_Init(&ctx); - MD5_Update(&ctx, (unsigned char *) data, len); - 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) { -#ifdef 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 - -#ifndef 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); -} - -#ifndef 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; - -#ifndef 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, " "); - } - -#ifndef 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 <sys/mman.h> -#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; -#ifndef 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)); - -#ifndef 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); - } -} - -#ifndef 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) { -#ifndef 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); -} - -#ifndef 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: - * <varint> // opcodes length - * <opcode>* - */ - 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; -} - -#ifndef 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; - -#ifndef 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)); -#ifndef 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); -#ifndef 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); - } -#ifndef 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 */ - -#ifndef 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; -#ifndef 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 defined(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 defined(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 -#ifndef 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 defined(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 defined(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 defined(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 defined(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 - -#ifndef 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 defined(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 defined(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 defined(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 defined(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_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; - } -} - -#ifndef 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; -#ifndef 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; - -#ifndef 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); -#ifdef 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 defined(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 defined(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 defined(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 defined(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 defined(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 defined(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 - 1; - 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 (size != 0) { - *buf = '\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 !defined(V7_DISABLE_FILENAMES) && !defined(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 <stdio.h> - -#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) { -#if 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) -#ifndef 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; -} - -#ifndef 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: <nil>\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 - -#ifndef 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; - -#ifndef 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') { -#ifndef 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) -#ifndef 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) && !defined(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) && !defined(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) { -#ifdef 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 <stdio.h> - -#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 defined(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 defined(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 defined(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 defined(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 defined(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 defined(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, -#ifdef V7_ENABLE_JS_GETTERS - fid_p_prop_1_getter, -#endif - fid_p_prop_2, -#ifdef 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)}, -#ifdef 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)}, -#ifdef 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; -} - -#ifndef 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); -#ifdef V7_ENABLE_JS_GETTERS - CR_DEFINE_ENTRY_POINT(fid_p_prop_1_getter); -#endif - CR_DEFINE_ENTRY_POINT(fid_p_prop_2); -#ifdef 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) -{ -#ifdef 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); -#ifdef 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 defined(V7_ENABLE_STACK_TRACKING) - struct stack_track_ctx stack_track_ctx; -#endif - int saved_line_no = v7->line_no; - -#if defined(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 = "<stdin>"; - 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 defined(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 - -#ifndef 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 - * - * -> - * - * <A> - * JMP_FALSE false - * <B> - * JMP end - * false: - * <C> - * 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 - * - * -> - * - * <A> - * JMP_FALSE end - * POP - * <B> - * 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 - * - * -> - * - * <A> - * DROP - * <B> - * DROP - * <C> - */ - 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:<B>, ...} - * - * -> - * - * CREATE_OBJ - * DUP - * PUSH_LIT "a" - * <B> - * 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: { - /* - * [<A>,,<B>,...] - * - * -> - * - * CREATE_ARR - * PUSH_ZERO - * - * 2DUP - * <A> - * SET - * POP - * PUSH_ONE - * ADD - * - * PUSH_ONE - * ADD - * - * 2DUP - * <B> - * ... - * 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... - * } - * - * -> - * - * <E> - * JMP_FALSE body - * <BT> - * JMP end - * body: - * <BF> - * 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: - * <B> - * cond: - * <C> - * 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 - * <TRY_B> - * OP_TRY_POP - * JMP finally - * catch: - * OP_TRY_POP - * OP_ENTER_CATCH <e> - * <CATCH_B> - * OP_EXIT_CATCH - * finally: - * OP_TRY_POP - * <FIN_B> - * OP_AFTER_FINALLY - * - * --------------- - * - * try { - * TRY_B - * } catch (e) { - * CATCH_B - * } - * - * -> - * OP_TRY_PUSH_CATCH catch - * <TRY_B> - * OP_TRY_POP - * JMP end - * catch: - * OP_TRY_POP - * OP_ENTER_CATCH <e> - * <CATCH_B> - * OP_EXIT_CATCH - * end: - * - * --------------- - * - * try { - * TRY_B - * } finally { - * FIN_B - * } - * - * -> - * OP_TRY_PUSH_FINALLY finally - * <TRY_B> - * finally: - * OP_TRY_POP - * <FIN_B> - * 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 - * <E> - * DUP - * <C1> - * EQ - * JMP_TRUE_DROP l1 - * DUP - * <C2> - * EQ - * JMP_TRUE_DROP l2 - * DROP - * JMP dfl - * - * dfl: - * <D> - * - * l1: - * <B1> - * - * l2: - * <B2> - * - * 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... - * } - * - * -> - * - * <INIT> - * DROP - * TRY_PUSH_LOOP end - * JMP cond - * body: - * <B> - * next: - * <IT> - * DROP - * cond: - * <COND> - * JMP_TRUE body - * end: - * JMP_IF_CONTINUE next - * TRY_POP - * - */ - case AST_FOR: { - ast_off_t iter, body, lookahead; - bcode_off_t end_label, continue_label, continue_target; - end = ast_get_skip(a, pos_after_tag, AST_END_SKIP); - body = ast_get_skip(a, pos_after_tag, AST_FOR_BODY_SKIP); - - lookahead = *ppos; - tag = fetch_tag(v7, bbuilder, a, &lookahead, &pos_after_tag); - /* - * Support for `var` declaration in INIT - */ - if (tag == AST_VAR) { - ast_off_t fvar_end; - lit_t lit; - - *ppos = lookahead; - fvar_end = ast_get_skip(a, pos_after_tag, AST_END_SKIP); - - /* - * Iterate through all vars in the given `var` declaration: they are - * just like assigments here - */ - while (*ppos < fvar_end) { - tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag); - /* Only var declarations are allowed (not function declarations) */ - V7_CHECK_INTERNAL(tag == AST_VAR_DECL); - lit = string_lit(bbuilder, a, pos_after_tag); - V7_TRY(compile_expr_builder(bbuilder, a, ppos)); - - /* Just like an assigment */ - bcode_op_lit(bbuilder, OP_SET_VAR, lit); - - /* INIT is stack-neutral */ - bcode_op(bbuilder, OP_DROP); - } - } else { - /* normal expression in INIT (not `var` declaration) */ - V7_TRY(compile_expr_builder(bbuilder, a, ppos)); - /* INIT is stack-neutral */ - bcode_op(bbuilder, OP_DROP); - } - cond = *ppos; - ast_skip_tree(a, ppos); - iter = *ppos; - *ppos = body; - - end_label = bcode_op_target(bbuilder, OP_TRY_PUSH_LOOP); - 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); - - V7_TRY(compile_expr_builder(bbuilder, a, &iter)); - bcode_op(bbuilder, OP_DROP); - - bcode_patch_target(bbuilder, cond_label, bcode_pos(bbuilder)); - - /* - * Handle for(INIT;;ITER) - */ - lookahead = cond; - tag = fetch_tag(v7, bbuilder, a, &lookahead, &pos_after_tag); - if (tag == AST_NOP) { - bcode_op(bbuilder, OP_JMP); - } else { - V7_TRY(compile_expr_builder(bbuilder, a, &cond)); - bcode_op(bbuilder, OP_JMP_TRUE); - } - body_label = bcode_add_target(bbuilder); - 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; - } - /* - * for(I in O) { - * B... - * } - * - * -> - * - * DUP - * <O> - * SWAP - * STASH - * DROP - * PUSH_PROP_ITER_CTX # push initial iteration context - * TRY_PUSH_LOOP brend - * loop: - * NEXT_PROP - * JMP_FALSE end - * SET_VAR <I> - * UNSTASH - * <B> - * next: - * STASH - * DROP - * JMP loop - * end: - * UNSTASH - * JMP try_pop: - * brend: - * # got here after a `break` or `continue` from a loop body: - * JMP_IF_CONTINUE next - * - * # we're not going to `continue`, so, need to remove an - * # extra stuff that was needed for the NEXT_PROP - * - * SWAP_DROP # drop iteration context - * SWAP_DROP # drop <O> - * SWAP_DROP # drop the value preceding the loop - * try_pop: - * TRY_POP - * - */ - case AST_FOR_IN: { - lit_t lit; - bcode_off_t loop_label, loop_target, end_label, brend_label, - continue_label, pop_label, continue_target; - ast_off_t end = ast_get_skip(a, pos_after_tag, AST_END_SKIP); - - tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag); - /* TODO(mkm) accept any l-value */ - if (tag == AST_VAR) { - tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag); - V7_CHECK_INTERNAL(tag == AST_VAR_DECL); - lit = string_lit(bbuilder, a, pos_after_tag); - ast_skip_tree(a, ppos); - } else { - V7_CHECK_INTERNAL(tag == AST_IDENT); - lit = string_lit(bbuilder, a, pos_after_tag); - } - - /* - * preserve previous statement value. - * We need to feed the previous value into the stash - * because it's required for the loop steady state. - * - * The stash register is required to simplify the steady state stack - * management, in particular the removal of value in 3rd position in case - * a of not taken exit. - * - * TODO(mkm): consider having a stash OP that moves a value to the stash - * register instead of copying it. The current behaviour has been - * optimized for the `assign` use case which seems more common. - */ - bcode_op(bbuilder, OP_DUP); - V7_TRY(compile_expr_builder(bbuilder, a, ppos)); - bcode_op(bbuilder, OP_SWAP); - bcode_op(bbuilder, OP_STASH); - bcode_op(bbuilder, OP_DROP); - - /* - * OP_NEXT_PROP needs the iteration context, let's push the initial one. - */ - bcode_op(bbuilder, OP_PUSH_PROP_ITER_CTX); - - brend_label = bcode_op_target(bbuilder, OP_TRY_PUSH_LOOP); - - /* loop: */ - loop_target = bcode_pos(bbuilder); - - /* - * The loop stead state begins with the following stack layout: - * `( S:v o h )` - */ - - bcode_op(bbuilder, OP_NEXT_PROP); - end_label = bcode_op_target(bbuilder, OP_JMP_FALSE); - bcode_op_lit(bbuilder, OP_SET_VAR, lit); - - /* - * The stash register contains the value of the previous statement, - * being it the statement before the for..in statement or - * the previous iteration. We move it to the data stack. It will - * be replaced by the values of the body statements as usual. - */ - bcode_op(bbuilder, OP_UNSTASH); - - /* - * This node is always a NOP, for compatibility - * with the layout of the AST_FOR node. - */ - ast_skip_tree(a, ppos); - - V7_TRY(compile_stmts(bbuilder, a, ppos, end)); - - continue_target = bcode_pos(bbuilder); - - /* - * Save the last body statement. If next evaluation of NEXT_PROP returns - * false, we'll unstash it. - */ - bcode_op(bbuilder, OP_STASH); - bcode_op(bbuilder, OP_DROP); - - loop_label = bcode_op_target(bbuilder, OP_JMP); - bcode_patch_target(bbuilder, loop_label, loop_target); - - /* end: */ - bcode_patch_target(bbuilder, end_label, bcode_pos(bbuilder)); - bcode_op(bbuilder, OP_UNSTASH); - - pop_label = bcode_op_target(bbuilder, OP_JMP); - - /* brend: */ - bcode_patch_target(bbuilder, brend_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_SWAP_DROP); - bcode_op(bbuilder, OP_SWAP_DROP); - bcode_op(bbuilder, OP_SWAP_DROP); - - /* try_pop: */ - bcode_patch_target(bbuilder, pop_label, bcode_pos(bbuilder)); - - bcode_op(bbuilder, OP_TRY_POP); - - bbuilder->v7->is_stack_neutral = 1; - break; - } - /* - * do { - * B... - * } while(COND); - * - * -> - * - * TRY_PUSH_LOOP end - * body: - * <B> - * cond: - * <COND> - * JMP_TRUE body - * end: - * JMP_IF_CONTINUE cond - * TRY_POP - * - */ - case AST_DOWHILE: { - bcode_off_t end_label, continue_label, continue_target; - end = ast_get_skip(a, pos_after_tag, AST_DO_WHILE_COND_SKIP); - end_label = bcode_op_target(bbuilder, OP_TRY_PUSH_LOOP); - body_target = bcode_pos(bbuilder); - V7_TRY(compile_stmts(bbuilder, a, ppos, end)); - - continue_target = bcode_pos(bbuilder); - V7_TRY(compile_expr_builder(bbuilder, a, ppos)); - 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_VAR: { - /* - * Var decls are hoisted when the function frame is created. Vars - * declared inside a `with` or `catch` block belong to the function - * lexical scope, and although those clauses create an inner frame - * no new variables should be created in it. A var decl thus - * behaves as a normal assignment at runtime. - */ - lit_t lit; - end = ast_get_skip(a, pos_after_tag, AST_END_SKIP); - while (*ppos < end) { - tag = fetch_tag(v7, bbuilder, a, ppos, &pos_after_tag); - if (tag == AST_FUNC_DECL) { - /* - * function declarations are already set during hoisting (see - * `compile_local_vars()`), so, skip it. - * - * Plus, they are stack-neutral, so don't forget to set - * `is_stack_neutral`. - */ - ast_skip_tree(a, ppos); - bbuilder->v7->is_stack_neutral = 1; - } else { - /* - * compile `var` declaration: basically it looks similar to an - * assignment, but it differs from an assignment is that it's - * stack-neutral: `1; var a = 5;` yields `1`, not `5`. - */ - V7_CHECK_INTERNAL(tag == AST_VAR_DECL); - lit = string_lit(bbuilder, a, pos_after_tag); - V7_TRY(compile_expr_builder(bbuilder, a, ppos)); - bcode_op_lit(bbuilder, OP_SET_VAR, lit); - - /* `var` declaration is stack-neutral */ - bcode_op(bbuilder, OP_DROP); - bbuilder->v7->is_stack_neutral = 1; - } - } - break; - } - case AST_RETURN: - bcode_op(bbuilder, OP_PUSH_UNDEFINED); - bcode_op(bbuilder, OP_RET); - break; - case AST_VALUE_RETURN: - V7_TRY(compile_expr_builder(bbuilder, a, ppos)); - bcode_op(bbuilder, OP_RET); - break; - default: - *ppos = pos_after_tag - 1; - V7_TRY(compile_expr_builder(bbuilder, a, ppos)); - } - -clean: - mbuf_free(&case_labels); - return rcode; -} - -static enum v7_err compile_body(struct bcode_builder *bbuilder, struct ast *a, - ast_off_t start, ast_off_t end, ast_off_t body, - ast_off_t fvar, ast_off_t *ppos) { - enum v7_err rcode = V7_OK; - struct v7 *v7 = bbuilder->v7; - -#ifndef V7_FORCE_STRICT_MODE - /* check 'use strict' */ - if (*ppos < end) { - ast_off_t tmp_pos = body; - if (fetch_tag(v7, bbuilder, a, &tmp_pos, NULL) == AST_USE_STRICT) { - bbuilder->bcode->strict_mode = 1; - /* move `body` offset, effectively removing `AST_USE_STRICT` from it */ - body = tmp_pos; - } - } -#endif - - /* put initial value for the function body execution */ - bcode_op(bbuilder, OP_PUSH_UNDEFINED); - - /* - * populate `bcode->ops` with function's local variable names. Note that we - * should do this *after* `OP_PUSH_UNDEFINED`, since `compile_local_vars` - * emits code that assigns the hoisted functions to local variables, and - * those statements assume that the stack contains `undefined`. - */ - V7_TRY(compile_local_vars(bbuilder, a, start, fvar)); - - /* compile body */ - *ppos = body; - V7_TRY(compile_stmts(bbuilder, a, ppos, end)); - -clean: - return rcode; -} - -/* - * Compiles a given script and populates a bcode structure. - * The AST must start with an AST_SCRIPT node. - */ -V7_PRIVATE enum v7_err compile_script(struct v7 *v7, struct ast *a, - struct bcode *bcode) { - ast_off_t pos_after_tag, end, fvar, pos = 0; - int saved_line_no = v7->line_no; - enum v7_err rcode = V7_OK; - struct bcode_builder bbuilder; - enum ast_tag tag; - - bcode_builder_init(v7, &bbuilder, bcode); - v7->line_no = 1; - - tag = fetch_tag(v7, &bbuilder, a, &pos, &pos_after_tag); - - /* first tag should always be AST_SCRIPT */ - assert(tag == AST_SCRIPT); - (void) tag; - - end = ast_get_skip(a, pos_after_tag, AST_END_SKIP); - fvar = ast_get_skip(a, pos_after_tag, AST_FUNC_FIRST_VAR_SKIP) - 1; - - V7_TRY(compile_body(&bbuilder, a, pos_after_tag - 1, end, pos /* body */, - fvar, &pos)); - -clean: - - bcode_builder_finalize(&bbuilder); - -#ifdef V7_BCODE_DUMP - if (rcode == V7_OK) { - fprintf(stderr, "--- script ---\n"); - dump_bcode(v7, stderr, bcode); - } -#endif - - v7->line_no = saved_line_no; - - return rcode; -} - -/* - * Compiles a given function and populates a bcode structure. - * The AST must contain an AST_FUNC node at offset ast_off. - */ -V7_PRIVATE enum v7_err compile_function(struct v7 *v7, struct ast *a, - ast_off_t *ppos, struct bcode *bcode) { - ast_off_t pos_after_tag, start, end, body, fvar; - const char *name; - size_t name_len; - size_t args_cnt; - enum v7_err rcode = V7_OK; - struct bcode_builder bbuilder; - enum ast_tag tag; - size_t names_end = 0; - bcode_builder_init(v7, &bbuilder, bcode); - tag = fetch_tag(v7, &bbuilder, a, ppos, &pos_after_tag); - start = pos_after_tag - 1; - - (void) tag; - assert(tag == AST_FUNC); - end = ast_get_skip(a, pos_after_tag, AST_END_SKIP); - body = ast_get_skip(a, pos_after_tag, AST_FUNC_BODY_SKIP); - fvar = ast_get_skip(a, pos_after_tag, AST_FUNC_FIRST_VAR_SKIP) - 1; - - /* retrieve function name */ - tag = fetch_tag(v7, &bbuilder, a, ppos, &pos_after_tag); - if (tag == AST_IDENT) { - /* function name is provided */ - name = ast_get_inlined_data(a, pos_after_tag, &name_len); - V7_TRY(bcode_add_name(&bbuilder, name, name_len, &names_end)); - } else { - /* no name: anonymous function */ - V7_TRY(bcode_add_name(&bbuilder, "", 0, &names_end)); - } - - /* retrieve function's argument names */ - for (args_cnt = 0; *ppos < body; args_cnt++) { - if (args_cnt > V7_ARGS_CNT_MAX) { - /* too many arguments */ - rcode = v7_throwf(v7, SYNTAX_ERROR, "Too many arguments"); - V7_THROW(V7_SYNTAX_ERROR); - } - - tag = fetch_tag(v7, &bbuilder, a, ppos, &pos_after_tag); - /* - * TODO(dfrank): it's not actually an internal error, we get here if - * we compile e.g. the following: (function(1){}) - */ - V7_CHECK_INTERNAL(tag == AST_IDENT); - name = ast_get_inlined_data(a, pos_after_tag, &name_len); - V7_TRY(bcode_add_name(&bbuilder, name, name_len, &names_end)); - } - - bcode->args_cnt = args_cnt; - bcode->func_name_present = 1; - - V7_TRY(compile_body(&bbuilder, a, start, end, body, fvar, ppos)); - -clean: - bcode_builder_finalize(&bbuilder); - -#ifdef V7_BCODE_DUMP - if (rcode == V7_OK) { - fprintf(stderr, "--- function ---\n"); - dump_bcode(v7, stderr, bcode); - } -#endif - - return rcode; -} - -V7_PRIVATE enum v7_err compile_expr(struct v7 *v7, struct ast *a, - ast_off_t *ppos, struct bcode *bcode) { - enum v7_err rcode = V7_OK; - struct bcode_builder bbuilder; - int saved_line_no = v7->line_no; - - bcode_builder_init(v7, &bbuilder, bcode); - v7->line_no = 1; - - rcode = compile_expr_builder(&bbuilder, a, ppos); - - bcode_builder_finalize(&bbuilder); - v7->line_no = saved_line_no; - return rcode; -} - -#endif /* V7_NO_COMPILER */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/stdlib.c" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* Amalgamated: #include "common/cs_strtod.h" */ - -/* Amalgamated: #include "v7/src/internal.h" */ -/* Amalgamated: #include "v7/src/core.h" */ -/* Amalgamated: #include "v7/src/primitive.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "v7/src/stdlib.h" */ -/* Amalgamated: #include "v7/src/std_array.h" */ -/* Amalgamated: #include "v7/src/std_boolean.h" */ -/* Amalgamated: #include "v7/src/std_date.h" */ -/* Amalgamated: #include "v7/src/std_error.h" */ -/* Amalgamated: #include "v7/src/std_function.h" */ -/* Amalgamated: #include "v7/src/std_json.h" */ -/* Amalgamated: #include "v7/src/std_math.h" */ -/* Amalgamated: #include "v7/src/std_number.h" */ -/* Amalgamated: #include "v7/src/std_object.h" */ -/* Amalgamated: #include "v7/src/std_regex.h" */ -/* Amalgamated: #include "v7/src/std_string.h" */ -/* Amalgamated: #include "v7/src/std_proxy.h" */ -/* Amalgamated: #include "v7/src/js_stdlib.h" */ -/* Amalgamated: #include "v7/src/object.h" */ -/* Amalgamated: #include "v7/src/string.h" */ -/* Amalgamated: #include "v7/src/util.h" */ -/* Amalgamated: #include "v7/src/exec.h" */ - -#ifdef NO_LIBC -void print_str(const char *str); -#endif - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Std_print(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - int i, num_args = v7_argc(v7); - val_t v; - - (void) res; - - for (i = 0; i < num_args; i++) { - v = v7_arg(v7, i); - if (v7_is_string(v)) { - size_t n; - const char *s = v7_get_string(v7, &v, &n); - printf("%.*s", (int) n, s); - } else { - v7_print(v7, v); - } - printf(" "); - } - printf(ENDL); - - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err std_eval(struct v7 *v7, v7_val_t arg, val_t this_obj, - int is_json, v7_val_t *res) { - enum v7_err rcode = V7_OK; - char buf[100], *p = buf; - struct v7_exec_opts opts; - memset(&opts, 0x00, sizeof(opts)); - opts.filename = "Eval'd code"; - - if (arg != V7_UNDEFINED) { - size_t len; - rcode = to_string(v7, arg, NULL, buf, sizeof(buf), &len); - if (rcode != V7_OK) { - goto clean; - } - - /* Fit null terminating byte and quotes */ - if (len >= sizeof(buf) - 2) { - /* Buffer is not large enough. Allocate a bigger one */ - p = (char *) malloc(len + 3); - rcode = to_string(v7, arg, NULL, p, len + 2, NULL); - if (rcode != V7_OK) { - goto clean; - } - } - - v7_set_gc_enabled(v7, 1); - if (is_json) { - opts.is_json = 1; - } else { - opts.this_obj = this_obj; - } - rcode = v7_exec_opt(v7, p, &opts, res); - if (rcode != V7_OK) { - goto clean; - } - } - -clean: - if (p != buf) { - free(p); - } - - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Std_eval(struct v7 *v7, v7_val_t *res) { - val_t this_obj = v7_get_this(v7); - v7_val_t arg = v7_arg(v7, 0); - return std_eval(v7, arg, this_obj, 0, res); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Std_parseInt(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - v7_val_t arg0 = V7_UNDEFINED; - v7_val_t arg1 = V7_UNDEFINED; - long sign = 1, base, n; - char buf[20], *p = buf, *end; - - *res = V7_TAG_NAN; - - arg0 = v7_arg(v7, 0); - arg1 = v7_arg(v7, 1); - - rcode = to_string(v7, arg0, &arg0, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - rcode = to_number_v(v7, arg1, &arg1); - if (rcode != V7_OK) { - goto clean; - } - - if (is_finite(v7, arg1)) { - base = v7_get_double(v7, arg1); - } else { - base = 0; - } - - if (base == 0) { - base = 10; - } - - if (base < 2 || base > 36) { - *res = V7_TAG_NAN; - goto clean; - } - - { - size_t str_len; - p = (char *) v7_get_string(v7, &arg0, &str_len); - } - - /* Strip leading whitespaces */ - while (*p != '\0' && isspace(*(unsigned char *) p)) { - p++; - } - - if (*p == '+') { - sign = 1; - p++; - } else if (*p == '-') { - sign = -1; - p++; - } - - if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { - base = 16; - p += 2; - } - - n = strtol(p, &end, base); - - *res = (p == end) ? V7_TAG_NAN : v7_mk_number(v7, n * sign); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Std_parseFloat(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - v7_val_t arg0 = V7_UNDEFINED; - char buf[20], *p = buf, *end; - double result; - - rcode = to_primitive(v7, v7_arg(v7, 0), V7_TO_PRIMITIVE_HINT_NUMBER, &arg0); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_is_string(arg0)) { - size_t str_len; - p = (char *) v7_get_string(v7, &arg0, &str_len); - } else { - rcode = to_string(v7, arg0, NULL, buf, sizeof(buf), NULL); - if (rcode != V7_OK) { - goto clean; - } - buf[sizeof(buf) - 1] = '\0'; - } - - while (*p != '\0' && isspace(*(unsigned char *) p)) { - p++; - } - - result = cs_strtod(p, &end); - - *res = (p == end) ? V7_TAG_NAN : v7_mk_number(v7, result); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Std_isNaN(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - v7_val_t arg0 = V7_TAG_NAN; - rcode = to_number_v(v7, v7_arg(v7, 0), &arg0); - if (rcode != V7_OK) { - goto clean; - } - - *res = v7_mk_boolean(v7, isnan(v7_get_double(v7, arg0))); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Std_isFinite(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - v7_val_t arg0 = V7_TAG_NAN; - - rcode = to_number_v(v7, v7_arg(v7, 0), &arg0); - if (rcode != V7_OK) { - goto clean; - } - - *res = v7_mk_boolean(v7, is_finite(v7, arg0)); - -clean: - return rcode; -} - -#ifndef NO_LIBC -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Std_exit(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - long exit_code; - - (void) res; - - rcode = to_long(v7, v7_arg(v7, 0), 0, &exit_code); - if (rcode != V7_OK) { - /* `to_long` has thrown, so, will return 1 */ - exit_code = 1; - } - exit(exit_code); - - return rcode; -} -#endif - -/* - * Initialize standard library. - * - * This function is used only internally, but used in a complicated mix of - * configurations, hence the commented V7_PRIVATE - */ -/*V7_PRIVATE*/ void init_stdlib(struct v7 *v7) { - v7_prop_attr_desc_t attr_internal = - (V7_DESC_ENUMERABLE(0) | V7_DESC_WRITABLE(0) | V7_DESC_CONFIGURABLE(0)); - - /* - * Ensure the first call to v7_mk_value will use a null proto: - * {}.__proto__.__proto__ == null - */ - v7->vals.object_prototype = mk_object(v7, V7_NULL); - v7->vals.array_prototype = v7_mk_object(v7); - v7->vals.boolean_prototype = v7_mk_object(v7); - v7->vals.string_prototype = v7_mk_object(v7); - v7->vals.regexp_prototype = v7_mk_object(v7); - v7->vals.number_prototype = v7_mk_object(v7); - v7->vals.error_prototype = v7_mk_object(v7); - v7->vals.global_object = v7_mk_object(v7); - v7->vals.date_prototype = v7_mk_object(v7); - v7->vals.function_prototype = v7_mk_object(v7); - v7->vals.proxy_prototype = v7_mk_object(v7); - - set_method(v7, v7->vals.global_object, "eval", Std_eval, 1); - set_method(v7, v7->vals.global_object, "print", Std_print, 1); -#ifndef NO_LIBC - set_method(v7, v7->vals.global_object, "exit", Std_exit, 1); -#endif - set_method(v7, v7->vals.global_object, "parseInt", Std_parseInt, 2); - set_method(v7, v7->vals.global_object, "parseFloat", Std_parseFloat, 1); - set_method(v7, v7->vals.global_object, "isNaN", Std_isNaN, 1); - set_method(v7, v7->vals.global_object, "isFinite", Std_isFinite, 1); - - v7_def(v7, v7->vals.global_object, "Infinity", 8, attr_internal, - v7_mk_number(v7, INFINITY)); - v7_set(v7, v7->vals.global_object, "global", 6, v7->vals.global_object); - - init_object(v7); - init_array(v7); - init_error(v7); - init_boolean(v7); -#if V7_ENABLE__Math - init_math(v7); -#endif - init_string(v7); -#if V7_ENABLE__RegExp - init_regex(v7); -#endif - init_number(v7); - init_json(v7); -#if V7_ENABLE__Date - init_date(v7); -#endif - init_function(v7); - init_js_stdlib(v7); - -#if V7_ENABLE__Proxy - init_proxy(v7); -#endif -} -#ifdef V7_MODULE_LINES -#line 1 "v7/src/js_stdlib.c" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* clang-format off */ -/* because clang-format would break JS code, e.g. === converted to == = ... */ - -/* Amalgamated: #include "v7/src/internal.h" */ -/* Amalgamated: #include "v7/src/core.h" */ -/* Amalgamated: #include "v7/src/exec.h" */ -/* Amalgamated: #include "v7/src/util.h" */ - -#define STRINGIFY(x) #x - -#if defined(__cplusplus) -extern "C" { -#endif /* __cplusplus */ - -static const char js_array_indexOf[] = STRINGIFY( - Object.defineProperty(Array.prototype, "indexOf", { - writable:true, - configurable: true, - value: function(a, x) { - var i; var r = -1; var b = +x; - if (!b || b < 0) b = 0; - for (i in this) if (i >= b && (r < 0 || i < r) && this[i] === a) r = +i; - return r; - }});); - -static const char js_array_lastIndexOf[] = STRINGIFY( - Object.defineProperty(Array.prototype, "lastIndexOf", { - writable:true, - configurable: true, - value: function(a, x) { - var i; var r = -1; var b = +x; - if (isNaN(b) || b < 0 || b >= this.length) b = this.length - 1; - for (i in this) if (i <= b && (r < 0 || i > r) && this[i] === a) r = +i; - return r; - }});); - -#if V7_ENABLE__Array__reduce -static const char js_array_reduce[] = STRINGIFY( - Object.defineProperty(Array.prototype, "reduce", { - writable:true, - configurable: true, - value: function(a, b) { - var f = 0; - if (typeof(a) != "function") { - throw new TypeError(a + " is not a function"); - } - for (var k in this) { - if (k > this.length) break; - if (f == 0 && b === undefined) { - b = this[k]; - f = 1; - } else { - b = a(b, this[k], k, this); - } - } - return b; - }});); -#endif - -static const char js_array_pop[] = STRINGIFY( - Object.defineProperty(Array.prototype, "pop", { - writable:true, - configurable: true, - value: function() { - var i = this.length - 1; - return this.splice(i, 1)[0]; - }});); - -static const char js_array_shift[] = STRINGIFY( - Object.defineProperty(Array.prototype, "shift", { - writable:true, - configurable: true, - value: function() { - return this.splice(0, 1)[0]; - }});); - -#if V7_ENABLE__Function__call -static const char js_function_call[] = STRINGIFY( - Object.defineProperty(Function.prototype, "call", { - writable:true, - configurable: true, - value: function() { - var t = arguments.splice(0, 1)[0]; - return this.apply(t, arguments); - }});); -#endif - -#if V7_ENABLE__Function__bind -static const char js_function_bind[] = STRINGIFY( - Object.defineProperty(Function.prototype, "bind", { - writable:true, - configurable: true, - value: function(t) { - var f = this; - return function() { - return f.apply(t, arguments); - }; - }});); -#endif - -#if V7_ENABLE__Blob -static const char js_Blob[] = STRINGIFY( - function Blob(a) { - this.a = a; - }); -#endif - -static const char * const js_functions[] = { -#if V7_ENABLE__Blob - js_Blob, -#endif -#if V7_ENABLE__Function__call - js_function_call, -#endif -#if V7_ENABLE__Function__bind - js_function_bind, -#endif -#if V7_ENABLE__Array__reduce - js_array_reduce, -#endif - js_array_indexOf, - js_array_lastIndexOf, - js_array_pop, - js_array_shift -}; - - V7_PRIVATE void init_js_stdlib(struct v7 *v7) { - val_t res; - int i; - - for(i = 0; i < (int) ARRAY_SIZE(js_functions); i++) { - if (v7_exec(v7, js_functions[i], &res) != V7_OK) { - fprintf(stderr, "ex: %s:\n", js_functions[i]); - v7_fprintln(stderr, v7, res); - } - } - - /* TODO(lsm): re-enable in a separate PR */ -#if 0 - v7_exec(v7, &res, STRINGIFY( - Array.prototype.unshift = function() { - var a = new Array(0, 0); - Array.prototype.push.apply(a, arguments); - Array.prototype.splice.apply(this, a); - return this.length; - };)); -#endif -} - -#if defined(__cplusplus) -} -#endif /* __cplusplus */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/slre.c" -#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 <http://www.gnu.org/licenses/>. - * - * 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 <https://www.cesanta.com/license>. - */ - -/* Amalgamated: #include "v7/src/v7_features.h" */ - -#include <setjmp.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#ifndef NO_LIBC -#include <ctype.h> -#endif - -/* Amalgamated: #include "common/utf.h" */ -/* Amalgamated: #include "v7/src/slre.h" */ - -/* Limitations */ -#define SLRE_MAX_RANGES 32 -#define SLRE_MAX_SETS 16 -#define SLRE_MAX_REP 0xFFFF - -#define SLRE_MALLOC malloc -#define SLRE_FREE free -#define SLRE_THROW(e, err_code) longjmp((e)->jmp_buf, (err_code)) - -static int hex(int c) { - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; - if (c >= 'A' && c <= 'F') return c - 'A' + 10; - return -SLRE_INVALID_HEX_DIGIT; -} - -int nextesc(const char **p) { - const unsigned char *s = (unsigned char *) (*p)++; - switch (*s) { - case 0: - return -SLRE_UNTERM_ESC_SEQ; - case 'c': - ++*p; - return *s & 31; - case 'b': - return '\b'; - case 't': - return '\t'; - case 'n': - return '\n'; - case 'v': - return '\v'; - case 'f': - return '\f'; - case 'r': - return '\r'; - case '\\': - return '\\'; - case 'u': - if (isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && - isxdigit(s[4])) { - (*p) += 4; - return hex(s[1]) << 12 | hex(s[2]) << 8 | hex(s[3]) << 4 | hex(s[4]); - } - return -SLRE_INVALID_HEX_DIGIT; - case 'x': - if (isxdigit(s[1]) && isxdigit(s[2])) { - (*p) += 2; - return (hex(s[1]) << 4) | hex(s[2]); - } - return -SLRE_INVALID_HEX_DIGIT; - default: - return -SLRE_INVALID_ESC_CHAR; - } -} - -#if V7_ENABLE__RegExp - -/* Parser Information */ -struct slre_node { - unsigned char type; - union { - Rune c; /* character */ - struct slre_class *cp; /* class pointer */ - struct { - struct slre_node *x; - union { - struct slre_node *y; - unsigned char n; - struct { - unsigned char ng; /* not greedy flag */ - unsigned short min; - unsigned short max; - } rp; - } y; - } xy; - } par; -}; - -struct slre_range { - unsigned short s, e; -}; - -/* character class, each pair of rune's defines a range */ -struct slre_class { - struct slre_range *end; - struct slre_range spans[SLRE_MAX_RANGES]; -}; - -struct slre_instruction { - unsigned char opcode; - union { - unsigned char n; - Rune c; /* character */ - struct slre_class *cp; /* class pointer */ - struct { - struct slre_instruction *x; - union { - struct { - unsigned short min; - unsigned short max; - } rp; - struct slre_instruction *y; - } y; - } xy; - } par; -}; - -struct slre_prog { - struct slre_instruction *start, *end; - unsigned int num_captures; - int flags; - struct slre_class charset[SLRE_MAX_SETS]; -}; - -struct slre_env { - int is_regex; - const char *src; - const char *src_end; - Rune curr_rune; - - struct slre_prog *prog; - struct slre_node *pstart, *pend; - - struct slre_node *caps[SLRE_MAX_CAPS]; - unsigned int num_captures; - unsigned int sets_num; - - int lookahead; - struct slre_class *curr_set; - int min_rep, max_rep; - -#if defined(__cplusplus) - ::jmp_buf jmp_buf; -#else - jmp_buf jmp_buf; -#endif -}; - -struct slre_thread { - struct slre_thread *prev; - struct slre_instruction *pc; - const char *start; - struct slre_loot loot; -}; - -enum slre_opcode { - I_END = 10, /* Terminate: match found */ - I_ANY, - P_ANY = I_ANY, /* Any character except newline, . */ - I_ANYNL, /* Any character including newline, . */ - I_BOL, - P_BOL = I_BOL, /* Beginning of line, ^ */ - I_CH, - P_CH = I_CH, - I_EOL, - P_EOL = I_EOL, /* End of line, $ */ - I_EOS, - P_EOS = I_EOS, /* End of string, \0 */ - I_JUMP, - I_LA, - P_LA = I_LA, - I_LA_N, - P_LA_N = I_LA_N, - I_LBRA, - P_BRA = I_LBRA, /* Left bracket, ( */ - I_REF, - P_REF = I_REF, - I_REP, - P_REP = I_REP, - I_REP_INI, - I_RBRA, /* Right bracket, ) */ - I_SET, - P_SET = I_SET, /* Character set, [] */ - I_SET_N, - P_SET_N = I_SET_N, /* Negated character set, [] */ - I_SPLIT, - I_WORD, - P_WORD = I_WORD, - I_WORD_N, - P_WORD_N = I_WORD_N, - P_ALT, /* Alternation, | */ - P_CAT, /* Concatentation, implicit operator */ - L_CH = 256, - L_COUNT, /* {M,N} */ - L_EOS, /* End of string, \0 */ - L_LA, /* "(?=" lookahead */ - L_LA_CAP, /* "(?:" lookahead, capture */ - L_LA_N, /* "(?!" negative lookahead */ - L_REF, /* "\1" back-reference */ - L_CHSET, /* character set */ - L_SET_N, /* negative character set */ - L_WORD, /* "\b" word boundary */ - L_WORD_N /* "\B" non-word boundary */ -}; - -static signed char dec(int c) { - if (isdigitrune(c)) return c - '0'; - return SLRE_INVALID_DEC_DIGIT; -} - -static unsigned char re_dec_digit(struct slre_env *e, int c) { - signed char ret = dec(c); - if (ret < 0) { - SLRE_THROW(e, SLRE_INVALID_DEC_DIGIT); - } - return ret; -} - -static int re_nextc(Rune *r, const char **src, const char *src_end) { - *r = 0; - if (*src >= src_end) return 0; - *src += chartorune(r, *src); - if (*r == '\\') { - const char *tmp_s = *src; - int i = nextesc(src); - switch (i) { - case -SLRE_INVALID_ESC_CHAR: - *r = '\\'; - *src = tmp_s; - *src += chartorune(r, *src); - break; - case -SLRE_INVALID_HEX_DIGIT: - default: - *r = i; - } - return 1; - } - return 0; -} - -static int re_nextc_raw(Rune *r, const char **src, const char *src_end) { - *r = 0; - if (*src >= src_end) return 0; - *src += chartorune(r, *src); - return 0; -} - -static int re_nextc_env(struct slre_env *e) { - return re_nextc(&e->curr_rune, &e->src, e->src_end); -} - -static void re_nchset(struct slre_env *e) { - if (e->sets_num >= nelem(e->prog->charset)) { - SLRE_THROW(e, SLRE_TOO_MANY_CHARSETS); - } - e->curr_set = e->prog->charset + e->sets_num++; - e->curr_set->end = e->curr_set->spans; -} - -static void re_rng2set(struct slre_env *e, Rune start, Rune end) { - if (start > end) { - SLRE_THROW(e, SLRE_INV_CHARSET_RANGE); - } - if (e->curr_set->end + 2 == e->curr_set->spans + nelem(e->curr_set->spans)) { - SLRE_THROW(e, SLRE_CHARSET_TOO_LARGE); - } - e->curr_set->end->s = start; - e->curr_set->end->e = end; - e->curr_set->end++; -} - -#define re_char2set(e, c) re_rng2set(e, c, c) - -#define re_d_2set(e) re_rng2set(e, '0', '9') - -static void re_D_2set(struct slre_env *e) { - re_rng2set(e, 0, '0' - 1); - re_rng2set(e, '9' + 1, 0xFFFF); -} - -static void re_s_2set(struct slre_env *e) { - re_char2set(e, 0x9); - re_rng2set(e, 0xA, 0xD); - re_char2set(e, 0x20); - re_char2set(e, 0xA0); - re_rng2set(e, 0x2028, 0x2029); - re_char2set(e, 0xFEFF); -} - -static void re_S_2set(struct slre_env *e) { - re_rng2set(e, 0, 0x9 - 1); - re_rng2set(e, 0xD + 1, 0x20 - 1); - re_rng2set(e, 0x20 + 1, 0xA0 - 1); - re_rng2set(e, 0xA0 + 1, 0x2028 - 1); - re_rng2set(e, 0x2029 + 1, 0xFEFF - 1); - re_rng2set(e, 0xFEFF + 1, 0xFFFF); -} - -static void re_w_2set(struct slre_env *e) { - re_d_2set(e); - re_rng2set(e, 'A', 'Z'); - re_char2set(e, '_'); - re_rng2set(e, 'a', 'z'); -} - -static void re_W_2set(struct slre_env *e) { - re_rng2set(e, 0, '0' - 1); - re_rng2set(e, '9' + 1, 'A' - 1); - re_rng2set(e, 'Z' + 1, '_' - 1); - re_rng2set(e, '_' + 1, 'a' - 1); - re_rng2set(e, 'z' + 1, 0xFFFF); -} - -static unsigned char re_endofcount(Rune c) { - switch (c) { - case ',': - case '}': - return 1; - } - return 0; -} - -static void re_ex_num_overfl(struct slre_env *e) { - SLRE_THROW(e, SLRE_NUM_OVERFLOW); -} - -static enum slre_opcode re_countrep(struct slre_env *e) { - e->min_rep = 0; - while (e->src < e->src_end && !re_endofcount(e->curr_rune = *e->src++)) { - e->min_rep = e->min_rep * 10 + re_dec_digit(e, e->curr_rune); - if (e->min_rep >= SLRE_MAX_REP) re_ex_num_overfl(e); - } - - if (e->curr_rune != ',') { - e->max_rep = e->min_rep; - return L_COUNT; - } - e->max_rep = 0; - while (e->src < e->src_end && (e->curr_rune = *e->src++) != '}') { - e->max_rep = e->max_rep * 10 + re_dec_digit(e, e->curr_rune); - if (e->max_rep >= SLRE_MAX_REP) re_ex_num_overfl(e); - } - if (!e->max_rep) { - e->max_rep = SLRE_MAX_REP; - return L_COUNT; - } - - return L_COUNT; -} - -static enum slre_opcode re_lexset(struct slre_env *e) { - Rune ch = 0; - unsigned char esc, ch_fl = 0, dash_fl = 0; - enum slre_opcode type = L_CHSET; - - re_nchset(e); - - esc = re_nextc_env(e); - if (!esc && e->curr_rune == '^') { - type = L_SET_N; - esc = re_nextc_env(e); - } - - for (; esc || e->curr_rune != ']'; esc = re_nextc_env(e)) { - if (!e->curr_rune) { - SLRE_THROW(e, SLRE_MALFORMED_CHARSET); - } - if (esc) { - if (strchr("DdSsWw", e->curr_rune)) { - if (ch_fl) { - re_char2set(e, ch); - if (dash_fl) re_char2set(e, '-'); - } - switch (e->curr_rune) { - case 'D': - re_D_2set(e); - break; - case 'd': - re_d_2set(e); - break; - case 'S': - re_S_2set(e); - break; - case 's': - re_s_2set(e); - break; - case 'W': - re_W_2set(e); - break; - case 'w': - re_w_2set(e); - break; - } - ch_fl = dash_fl = 0; - continue; - } - switch (e->curr_rune) { - default: - /* case '-': - case '\\': - case '.': - case '/': - case ']': - case '|': */ - break; - case '0': - e->curr_rune = 0; - break; - case 'b': - e->curr_rune = '\b'; - break; - /* default: - SLRE_THROW(e->catch_point, e->err_msg, - SLRE_INVALID_ESC_CHAR); */ - } - } else { - if (e->curr_rune == '-') { - if (ch_fl) { - if (dash_fl) { - re_rng2set(e, ch, '-'); - ch_fl = dash_fl = 0; - } else - dash_fl = 1; - } else { - ch = '-'; - ch_fl = 1; - } - continue; - } - } - if (ch_fl) { - if (dash_fl) { - re_rng2set(e, ch, e->curr_rune); - ch_fl = dash_fl = 0; - } else { - re_char2set(e, ch); - ch = e->curr_rune; - } - } else { - ch = e->curr_rune; - ch_fl = 1; - } - } - if (ch_fl) { - re_char2set(e, ch); - if (dash_fl) re_char2set(e, '-'); - } - return type; -} - -static int re_lexer(struct slre_env *e) { - if (re_nextc_env(e)) { - switch (e->curr_rune) { - case '0': - e->curr_rune = 0; - return L_EOS; - case 'b': - return L_WORD; - case 'B': - return L_WORD_N; - case 'd': - re_nchset(e); - re_d_2set(e); - return L_CHSET; - case 'D': - re_nchset(e); - re_d_2set(e); - return L_SET_N; - case 's': - re_nchset(e); - re_s_2set(e); - return L_CHSET; - case 'S': - re_nchset(e); - re_s_2set(e); - return L_SET_N; - case 'w': - re_nchset(e); - re_w_2set(e); - return L_CHSET; - case 'W': - re_nchset(e); - re_w_2set(e); - return L_SET_N; - } - if (isdigitrune(e->curr_rune)) { - e->curr_rune -= '0'; - if (isdigitrune(*e->src)) - e->curr_rune = e->curr_rune * 10 + *e->src++ - '0'; - return L_REF; - } - return L_CH; - } - - if (e->is_regex) { - switch (e->curr_rune) { - case 0: - return 0; - case '$': - case ')': - case '*': - case '+': - case '.': - case '?': - case '^': - case '|': - return e->curr_rune; - case '{': - return re_countrep(e); - case '[': - return re_lexset(e); - case '(': - if (e->src[0] == '?') switch (e->src[1]) { - case '=': - e->src += 2; - return L_LA; - case ':': - e->src += 2; - return L_LA_CAP; - case '!': - e->src += 2; - return L_LA_N; - } - return '('; - } - } else if (e->curr_rune == 0) { - return 0; - } - - return L_CH; -} - -#define RE_NEXT(env) (env)->lookahead = re_lexer(env) -#define RE_ACCEPT(env, t) ((env)->lookahead == (t) ? RE_NEXT(env), 1 : 0) - -static struct slre_node *re_nnode(struct slre_env *e, int type) { - memset(e->pend, 0, sizeof(struct slre_node)); - e->pend->type = type; - return e->pend++; -} - -static unsigned char re_isemptynd(struct slre_node *nd) { - if (!nd) return 1; - switch (nd->type) { - default: - return 1; - case P_ANY: - case P_CH: - case P_SET: - case P_SET_N: - return 0; - case P_BRA: - case P_REF: - return re_isemptynd(nd->par.xy.x); - case P_CAT: - return re_isemptynd(nd->par.xy.x) && re_isemptynd(nd->par.xy.y.y); - case P_ALT: - return re_isemptynd(nd->par.xy.x) || re_isemptynd(nd->par.xy.y.y); - case P_REP: - return re_isemptynd(nd->par.xy.x) || !nd->par.xy.y.rp.min; - } -} - -static struct slre_node *re_nrep(struct slre_env *e, struct slre_node *nd, - int ng, unsigned short min, - unsigned short max) { - struct slre_node *rep = re_nnode(e, P_REP); - if (max == SLRE_MAX_REP && re_isemptynd(nd)) { - SLRE_THROW(e, SLRE_INF_LOOP_M_EMP_STR); - } - rep->par.xy.y.rp.ng = ng; - rep->par.xy.y.rp.min = min; - rep->par.xy.y.rp.max = max; - rep->par.xy.x = nd; - return rep; -} - -static struct slre_node *re_parser(struct slre_env *e); - -static struct slre_node *re_parse_la(struct slre_env *e) { - struct slre_node *nd; - int min, max; - switch (e->lookahead) { - case '^': - RE_NEXT(e); - return re_nnode(e, P_BOL); - case '$': - RE_NEXT(e); - return re_nnode(e, P_EOL); - case L_EOS: - RE_NEXT(e); - return re_nnode(e, P_EOS); - case L_WORD: - RE_NEXT(e); - return re_nnode(e, P_WORD); - case L_WORD_N: - RE_NEXT(e); - return re_nnode(e, P_WORD_N); - } - - switch (e->lookahead) { - case L_CH: - nd = re_nnode(e, P_CH); - nd->par.c = e->curr_rune; - RE_NEXT(e); - break; - case L_CHSET: - nd = re_nnode(e, P_SET); - nd->par.cp = e->curr_set; - RE_NEXT(e); - break; - case L_SET_N: - nd = re_nnode(e, P_SET_N); - nd->par.cp = e->curr_set; - RE_NEXT(e); - break; - case L_REF: - nd = re_nnode(e, P_REF); - if (!e->curr_rune || e->curr_rune > e->num_captures || - !e->caps[e->curr_rune]) { - SLRE_THROW(e, SLRE_INVALID_BACK_REFERENCE); - } - nd->par.xy.y.n = e->curr_rune; - nd->par.xy.x = e->caps[e->curr_rune]; - RE_NEXT(e); - break; - case '.': - RE_NEXT(e); - nd = re_nnode(e, P_ANY); - break; - case '(': - RE_NEXT(e); - nd = re_nnode(e, P_BRA); - if (e->num_captures == SLRE_MAX_CAPS) { - SLRE_THROW(e, SLRE_TOO_MANY_CAPTURES); - } - nd->par.xy.y.n = e->num_captures++; - nd->par.xy.x = re_parser(e); - e->caps[nd->par.xy.y.n] = nd; - if (!RE_ACCEPT(e, ')')) { - SLRE_THROW(e, SLRE_UNMATCH_LBR); - } - break; - case L_LA: - RE_NEXT(e); - nd = re_nnode(e, P_LA); - nd->par.xy.x = re_parser(e); - if (!RE_ACCEPT(e, ')')) { - SLRE_THROW(e, SLRE_UNMATCH_LBR); - } - break; - case L_LA_CAP: - RE_NEXT(e); - nd = re_parser(e); - if (!RE_ACCEPT(e, ')')) { - SLRE_THROW(e, SLRE_UNMATCH_LBR); - } - break; - case L_LA_N: - RE_NEXT(e); - nd = re_nnode(e, P_LA_N); - nd->par.xy.x = re_parser(e); - if (!RE_ACCEPT(e, ')')) { - SLRE_THROW(e, SLRE_UNMATCH_LBR); - } - break; - default: - SLRE_THROW(e, SLRE_SYNTAX_ERROR); - } - - switch (e->lookahead) { - case '*': - RE_NEXT(e); - return re_nrep(e, nd, RE_ACCEPT(e, '?'), 0, SLRE_MAX_REP); - case '+': - RE_NEXT(e); - return re_nrep(e, nd, RE_ACCEPT(e, '?'), 1, SLRE_MAX_REP); - case '?': - RE_NEXT(e); - return re_nrep(e, nd, RE_ACCEPT(e, '?'), 0, 1); - case L_COUNT: - min = e->min_rep, max = e->max_rep; - RE_NEXT(e); - if (max < min) { - SLRE_THROW(e, SLRE_INVALID_QUANTIFIER); - } - return re_nrep(e, nd, RE_ACCEPT(e, '?'), min, max); - } - return nd; -} - -static unsigned char re_endofcat(Rune c, int is_regex) { - switch (c) { - case 0: - return 1; - case '|': - case ')': - if (is_regex) return 1; - } - return 0; -} - -static struct slre_node *re_parser(struct slre_env *e) { - struct slre_node *alt = NULL, *cat, *nd; - if (!re_endofcat(e->lookahead, e->is_regex)) { - cat = re_parse_la(e); - while (!re_endofcat(e->lookahead, e->is_regex)) { - nd = cat; - cat = re_nnode(e, P_CAT); - cat->par.xy.x = nd; - cat->par.xy.y.y = re_parse_la(e); - } - alt = cat; - } - if (e->lookahead == '|') { - RE_NEXT(e); - nd = alt; - alt = re_nnode(e, P_ALT); - alt->par.xy.x = nd; - alt->par.xy.y.y = re_parser(e); - } - return alt; -} - -static unsigned int re_nodelen(struct slre_node *nd) { - unsigned int n = 0; - if (!nd) return 0; - switch (nd->type) { - case P_ALT: - n = 2; - case P_CAT: - return re_nodelen(nd->par.xy.x) + re_nodelen(nd->par.xy.y.y) + n; - case P_BRA: - case P_LA: - case P_LA_N: - return re_nodelen(nd->par.xy.x) + 2; - case P_REP: - n = nd->par.xy.y.rp.max - nd->par.xy.y.rp.min; - switch (nd->par.xy.y.rp.min) { - case 0: - if (!n) return 0; - if (nd->par.xy.y.rp.max >= SLRE_MAX_REP) - return re_nodelen(nd->par.xy.x) + 2; - case 1: - if (!n) return re_nodelen(nd->par.xy.x); - if (nd->par.xy.y.rp.max >= SLRE_MAX_REP) - return re_nodelen(nd->par.xy.x) + 1; - default: - n = 4; - if (nd->par.xy.y.rp.max >= SLRE_MAX_REP) n++; - return re_nodelen(nd->par.xy.x) + n; - } - default: - return 1; - } -} - -static struct slre_instruction *re_newinst(struct slre_prog *prog, int opcode) { - memset(prog->end, 0, sizeof(struct slre_instruction)); - prog->end->opcode = opcode; - return prog->end++; -} - -static void re_compile(struct slre_env *e, struct slre_node *nd) { - struct slre_instruction *inst, *split, *jump, *rep; - unsigned int n; - - if (!nd) return; - - switch (nd->type) { - case P_ALT: - split = re_newinst(e->prog, I_SPLIT); - re_compile(e, nd->par.xy.x); - jump = re_newinst(e->prog, I_JUMP); - re_compile(e, nd->par.xy.y.y); - split->par.xy.x = split + 1; - split->par.xy.y.y = jump + 1; - jump->par.xy.x = e->prog->end; - break; - - case P_ANY: - re_newinst(e->prog, I_ANY); - break; - - case P_BOL: - re_newinst(e->prog, I_BOL); - break; - - case P_BRA: - inst = re_newinst(e->prog, I_LBRA); - inst->par.n = nd->par.xy.y.n; - re_compile(e, nd->par.xy.x); - inst = re_newinst(e->prog, I_RBRA); - inst->par.n = nd->par.xy.y.n; - break; - - case P_CAT: - re_compile(e, nd->par.xy.x); - re_compile(e, nd->par.xy.y.y); - break; - - case P_CH: - inst = re_newinst(e->prog, I_CH); - inst->par.c = nd->par.c; - break; - - case P_EOL: - re_newinst(e->prog, I_EOL); - break; - - case P_EOS: - re_newinst(e->prog, I_EOS); - break; - - case P_LA: - split = re_newinst(e->prog, I_LA); - re_compile(e, nd->par.xy.x); - re_newinst(e->prog, I_END); - split->par.xy.x = split + 1; - split->par.xy.y.y = e->prog->end; - break; - case P_LA_N: - split = re_newinst(e->prog, I_LA_N); - re_compile(e, nd->par.xy.x); - re_newinst(e->prog, I_END); - split->par.xy.x = split + 1; - split->par.xy.y.y = e->prog->end; - break; - - case P_REF: - inst = re_newinst(e->prog, I_REF); - inst->par.n = nd->par.xy.y.n; - break; - - case P_REP: - n = nd->par.xy.y.rp.max - nd->par.xy.y.rp.min; - switch (nd->par.xy.y.rp.min) { - case 0: - if (!n) break; - if (nd->par.xy.y.rp.max >= SLRE_MAX_REP) { - split = re_newinst(e->prog, I_SPLIT); - re_compile(e, nd->par.xy.x); - jump = re_newinst(e->prog, I_JUMP); - jump->par.xy.x = split; - split->par.xy.x = split + 1; - split->par.xy.y.y = e->prog->end; - if (nd->par.xy.y.rp.ng) { - split->par.xy.y.y = split + 1; - split->par.xy.x = e->prog->end; - } - break; - } - case 1: - if (!n) { - re_compile(e, nd->par.xy.x); - break; - } - if (nd->par.xy.y.rp.max >= SLRE_MAX_REP) { - inst = e->prog->end; - re_compile(e, nd->par.xy.x); - split = re_newinst(e->prog, I_SPLIT); - split->par.xy.x = inst; - split->par.xy.y.y = e->prog->end; - if (nd->par.xy.y.rp.ng) { - split->par.xy.y.y = inst; - split->par.xy.x = e->prog->end; - } - break; - } - default: - inst = re_newinst(e->prog, I_REP_INI); - inst->par.xy.y.rp.min = nd->par.xy.y.rp.min; - inst->par.xy.y.rp.max = n; - rep = re_newinst(e->prog, I_REP); - split = re_newinst(e->prog, I_SPLIT); - re_compile(e, nd->par.xy.x); - jump = re_newinst(e->prog, I_JUMP); - jump->par.xy.x = rep; - rep->par.xy.x = e->prog->end; - split->par.xy.x = split + 1; - split->par.xy.y.y = e->prog->end; - if (nd->par.xy.y.rp.ng) { - split->par.xy.y.y = split + 1; - split->par.xy.x = e->prog->end; - } - if (nd->par.xy.y.rp.max >= SLRE_MAX_REP) { - inst = split + 1; - split = re_newinst(e->prog, I_SPLIT); - split->par.xy.x = inst; - split->par.xy.y.y = e->prog->end; - if (nd->par.xy.y.rp.ng) { - split->par.xy.y.y = inst; - split->par.xy.x = e->prog->end; - } - break; - } - break; - } - break; - - case P_SET: - inst = re_newinst(e->prog, I_SET); - inst->par.cp = nd->par.cp; - break; - case P_SET_N: - inst = re_newinst(e->prog, I_SET_N); - inst->par.cp = nd->par.cp; - break; - - case P_WORD: - re_newinst(e->prog, I_WORD); - break; - case P_WORD_N: - re_newinst(e->prog, I_WORD_N); - break; - } -} - -#ifdef RE_TEST -static void print_set(struct slre_class *cp) { - struct slre_range *p; - for (p = cp->spans; p < cp->end; p++) { - printf("%s", p == cp->spans ? "'" : ",'"); - printf( - p->s >= 32 && p->s < 127 ? "%c" : (p->s < 256 ? "\\x%02X" : "\\u%04X"), - p->s); - if (p->s != p->e) { - printf(p->e >= 32 && p->e < 127 ? "-%c" - : (p->e < 256 ? "-\\x%02X" : "-\\u%04X"), - p->e); - } - printf("'"); - } - printf("]"); -} - -static void node_print(struct slre_node *nd) { - if (!nd) { - printf("Empty"); - return; - } - switch (nd->type) { - case P_ALT: - printf("{"); - node_print(nd->par.xy.x); - printf(" | "); - node_print(nd->par.xy.y.y); - printf("}"); - break; - case P_ANY: - printf("."); - break; - case P_BOL: - printf("^"); - break; - case P_BRA: - node_print(nd->par.xy.x); - printf(")"); - break; - case P_CAT: - printf("{"); - node_print(nd->par.xy.x); - printf(" & "); - node_print(nd->par.xy.y.y); - printf("}"); - break; - case P_CH: - printf(nd->par.c >= 32 && nd->par.c < 127 ? "'%c'" : "'\\u%04X'", - nd->par.c); - break; - case P_EOL: - printf("$"); - break; - case P_EOS: - printf("\\0"); - break; - case P_LA: - printf("LA("); - node_print(nd->par.xy.x); - printf(")"); - break; - case P_LA_N: - printf("LA_N("); - node_print(nd->par.xy.x); - printf(")"); - break; - case P_REF: - printf("\\%d", nd->par.xy.y.n); - break; - case P_REP: - node_print(nd->par.xy.x); - printf(nd->par.xy.y.rp.ng ? "{%d,%d}?" : "{%d,%d}", nd->par.xy.y.rp.min, - nd->par.xy.y.rp.max); - break; - case P_SET: - printf("["); - print_set(nd->par.cp); - break; - case P_SET_N: - printf("[^"); - print_set(nd->par.cp); - break; - case P_WORD: - printf("\\b"); - break; - case P_WORD_N: - printf("\\B"); - break; - } -} - -static void program_print(struct slre_prog *prog) { - struct slre_instruction *inst; - for (inst = prog->start; inst < prog->end; ++inst) { - printf("%3d: ", inst - prog->start); - switch (inst->opcode) { - case I_END: - puts("end"); - break; - case I_ANY: - puts("."); - break; - case I_ANYNL: - puts(". | '\\r' | '\\n'"); - break; - case I_BOL: - puts("^"); - break; - case I_CH: - printf( - inst->par.c >= 32 && inst->par.c < 127 ? "'%c'\n" : "'\\u%04X'\n", - inst->par.c); - break; - case I_EOL: - puts("$"); - break; - case I_EOS: - puts("\\0"); - break; - case I_JUMP: - printf("-->%d\n", inst->par.xy.x - prog->start); - break; - case I_LA: - printf("la %d %d\n", inst->par.xy.x - prog->start, - inst->par.xy.y.y - prog->start); - break; - case I_LA_N: - printf("la_n %d %d\n", inst->par.xy.x - prog->start, - inst->par.xy.y.y - prog->start); - break; - case I_LBRA: - printf("( %d\n", inst->par.n); - break; - case I_RBRA: - printf(") %d\n", inst->par.n); - break; - case I_SPLIT: - printf("-->%d | -->%d\n", inst->par.xy.x - prog->start, - inst->par.xy.y.y - prog->start); - break; - case I_REF: - printf("\\%d\n", inst->par.n); - break; - case I_REP: - printf("repeat -->%d\n", inst->par.xy.x - prog->start); - break; - case I_REP_INI: - printf("init_rep %d %d\n", inst->par.xy.y.rp.min, - inst->par.xy.y.rp.min + inst->par.xy.y.rp.max); - break; - case I_SET: - printf("["); - print_set(inst->par.cp); - puts(""); - break; - case I_SET_N: - printf("[^"); - print_set(inst->par.cp); - puts(""); - break; - case I_WORD: - puts("\\w"); - break; - case I_WORD_N: - puts("\\W"); - break; - } - } -} -#endif - -int slre_compile(const char *pat, size_t pat_len, const char *flags, - volatile size_t fl_len, struct slre_prog **pr, int is_regex) { - struct slre_env e; - struct slre_node *nd; - struct slre_instruction *split, *jump; - int err_code; - - e.is_regex = is_regex; - e.prog = (struct slre_prog *) SLRE_MALLOC(sizeof(struct slre_prog)); - e.pstart = e.pend = - (struct slre_node *) SLRE_MALLOC(sizeof(struct slre_node) * pat_len * 2); - e.prog->flags = is_regex ? SLRE_FLAG_RE : 0; - - if ((err_code = setjmp(e.jmp_buf)) != SLRE_OK) { - SLRE_FREE(e.pstart); - SLRE_FREE(e.prog); - return err_code; - } - - while (fl_len--) { - switch (flags[fl_len]) { - case 'g': - e.prog->flags |= SLRE_FLAG_G; - break; - case 'i': - e.prog->flags |= SLRE_FLAG_I; - break; - case 'm': - e.prog->flags |= SLRE_FLAG_M; - break; - } - } - - e.src = pat; - e.src_end = pat + pat_len; - e.sets_num = 0; - e.num_captures = 1; - /*e.flags = flags;*/ - memset(e.caps, 0, sizeof(e.caps)); - - RE_NEXT(&e); - nd = re_parser(&e); - if (e.lookahead == ')') { - SLRE_THROW(&e, SLRE_UNMATCH_RBR); - } - if (e.lookahead != 0) { - SLRE_THROW(&e, SLRE_SYNTAX_ERROR); - } - - e.prog->num_captures = e.num_captures; - e.prog->start = e.prog->end = (struct slre_instruction *) SLRE_MALLOC( - (re_nodelen(nd) + 6) * sizeof(struct slre_instruction)); - - split = re_newinst(e.prog, I_SPLIT); - split->par.xy.x = split + 3; - split->par.xy.y.y = split + 1; - re_newinst(e.prog, I_ANYNL); - jump = re_newinst(e.prog, I_JUMP); - jump->par.xy.x = split; - re_newinst(e.prog, I_LBRA); - re_compile(&e, nd); - re_newinst(e.prog, I_RBRA); - re_newinst(e.prog, I_END); - -#ifdef RE_TEST - node_print(nd); - putchar('\n'); - program_print(e.prog); -#endif - - SLRE_FREE(e.pstart); - - if (pr != NULL) { - *pr = e.prog; - } else { - slre_free(e.prog); - } - - return err_code; -} - -void slre_free(struct slre_prog *prog) { - if (prog) { - SLRE_FREE(prog->start); - SLRE_FREE(prog); - } -} - -static struct slre_thread *re_newthread(struct slre_thread *t, - struct slre_instruction *pc, - const char *start, - struct slre_loot *loot) { - struct slre_thread *new_thread = - (struct slre_thread *) SLRE_MALLOC(sizeof(struct slre_thread)); - if (new_thread != NULL) new_thread->prev = t; - t->pc = pc; - t->start = start; - t->loot = *loot; - return new_thread; -} - -static struct slre_thread *get_prev_thread(struct slre_thread *t) { - struct slre_thread *tmp_thr = t->prev; - SLRE_FREE(t); - return tmp_thr; -} - -static void free_threads(struct slre_thread *t) { - while (t->prev != NULL) t = get_prev_thread(t); -} - -static unsigned char re_match(struct slre_instruction *pc, const char *current, - const char *end, const char *bol, - unsigned int flags, struct slre_loot *loot) { - struct slre_loot sub, tmpsub; - Rune c, r; - struct slre_range *p; - size_t i; - struct slre_thread thread, *curr_thread, *tmp_thr; - - /* queue initial thread */ - thread.prev = NULL; - curr_thread = re_newthread(&thread, pc, current, loot); - - /* run threads in stack order */ - do { - curr_thread = get_prev_thread(curr_thread); - pc = curr_thread->pc; - current = curr_thread->start; - sub = curr_thread->loot; - for (;;) { - switch (pc->opcode) { - case I_END: - memcpy(loot->caps, sub.caps, sizeof loot->caps); - free_threads(curr_thread); - return 1; - case I_ANY: - case I_ANYNL: - if (current < end) { - current += chartorune(&c, current); - if (c && !(pc->opcode == I_ANY && isnewline(c))) break; - } - goto no_match; - - case I_BOL: - if (current == bol) break; - if ((flags & SLRE_FLAG_M) && isnewline(current[-1])) break; - goto no_match; - case I_CH: - if (current < end) { - current += chartorune(&c, current); - if (c && - (c == pc->par.c || ((flags & SLRE_FLAG_I) && - tolowerrune(c) == tolowerrune(pc->par.c)))) - break; - } - goto no_match; - case I_EOL: - if (current >= end) break; - if ((flags & SLRE_FLAG_M) && isnewline(*current)) break; - goto no_match; - case I_EOS: - if (current >= end) break; - goto no_match; - - case I_JUMP: - pc = pc->par.xy.x; - continue; - - case I_LA: - if (re_match(pc->par.xy.x, current, end, bol, flags, &sub)) { - pc = pc->par.xy.y.y; - continue; - } - goto no_match; - case I_LA_N: - tmpsub = sub; - if (!re_match(pc->par.xy.x, current, end, bol, flags, &tmpsub)) { - pc = pc->par.xy.y.y; - continue; - } - goto no_match; - - case I_LBRA: - sub.caps[pc->par.n].start = current; - break; - - case I_REF: - i = sub.caps[pc->par.n].end - sub.caps[pc->par.n].start; - if (flags & SLRE_FLAG_I) { - int num = i; - const char *s = current, *p = sub.caps[pc->par.n].start; - Rune rr; - for (; num && *s && *p; num--) { - s += chartorune(&r, s); - p += chartorune(&rr, p); - if (tolowerrune(r) != tolowerrune(rr)) break; - } - if (num) goto no_match; - } else if (strncmp(current, sub.caps[pc->par.n].start, i)) { - goto no_match; - } - if (i > 0) current += i; - break; - - case I_REP: - if (pc->par.xy.y.rp.min) { - pc->par.xy.y.rp.min--; - pc++; - } else if (!pc->par.xy.y.rp.max--) { - pc = pc->par.xy.x; - continue; - } - break; - - case I_REP_INI: - (pc + 1)->par.xy.y.rp.min = pc->par.xy.y.rp.min; - (pc + 1)->par.xy.y.rp.max = pc->par.xy.y.rp.max; - break; - - case I_RBRA: - sub.caps[pc->par.n].end = current; - break; - - case I_SET: - case I_SET_N: - if (current >= end) goto no_match; - current += chartorune(&c, current); - if (!c) goto no_match; - - i = 1; - for (p = pc->par.cp->spans; i && p < pc->par.cp->end; p++) - if (flags & SLRE_FLAG_I) { - for (r = p->s; r <= p->e; ++r) - if (tolowerrune(c) == tolowerrune(r)) { - i = 0; - break; - } - } else if (p->s <= c && c <= p->e) - i = 0; - - if (pc->opcode == I_SET) i = !i; - if (i) break; - goto no_match; - - case I_SPLIT: - tmp_thr = curr_thread; - curr_thread = - re_newthread(curr_thread, pc->par.xy.y.y, current, &sub); - if (curr_thread == NULL) { - fprintf(stderr, "re_match: no memory for thread!\n"); - free_threads(tmp_thr); - return 0; - } - pc = pc->par.xy.x; - continue; - - case I_WORD: - case I_WORD_N: - i = (current > bol && iswordchar(current[-1])); - if (iswordchar(current[0])) i = !i; - if (pc->opcode == I_WORD_N) i = !i; - if (i) break; - /* goto no_match; */ - - default: - goto no_match; - } - pc++; - } - no_match: - ; - } while (curr_thread->prev != NULL); - return 0; -} - -int slre_exec(struct slre_prog *prog, int flag_g, const char *start, - const char *end, struct slre_loot *loot) { - struct slre_loot tmpsub; - const char *st = start; - - if (!loot) loot = &tmpsub; - memset(loot, 0, sizeof(*loot)); - - if (!flag_g) { - loot->num_captures = prog->num_captures; - return !re_match(prog->start, start, end, start, prog->flags, loot); - } - - while (re_match(prog->start, st, end, start, prog->flags, &tmpsub)) { - unsigned int i; - st = tmpsub.caps[0].end; - for (i = 0; i < prog->num_captures; i++) { - struct slre_cap *l = &loot->caps[loot->num_captures + i]; - struct slre_cap *s = &tmpsub.caps[i]; - l->start = s->start; - l->end = s->end; - } - loot->num_captures += prog->num_captures; - } - return !loot->num_captures; -} - -int slre_replace(struct slre_loot *loot, const char *src, size_t src_len, - const char *rstr, size_t rstr_len, struct slre_loot *dstsub) { - int size = 0, n; - Rune curr_rune; - const char *const rstr_end = rstr + rstr_len; - - memset(dstsub, 0, sizeof(*dstsub)); - while (rstr < rstr_end && !(n = re_nextc_raw(&curr_rune, &rstr, rstr_end)) && - curr_rune) { - int sz; - if (n < 0) return n; - if (curr_rune == '$') { - n = re_nextc(&curr_rune, &rstr, rstr_end); - if (n < 0) return n; - switch (curr_rune) { - case '&': - sz = loot->caps[0].end - loot->caps[0].start; - size += sz; - dstsub->caps[dstsub->num_captures++] = loot->caps[0]; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - int sbn = dec(curr_rune); - if (0 == sbn && rstr[0] && isdigitrune(rstr[0])) { - n = re_nextc(&curr_rune, &rstr, rstr_end); - if (n < 0) return n; - sz = dec(curr_rune); - sbn = sbn * 10 + sz; - } - if (sbn >= loot->num_captures) break; - sz = loot->caps[sbn].end - loot->caps[sbn].start; - size += sz; - dstsub->caps[dstsub->num_captures++] = loot->caps[sbn]; - break; - } - case '`': - sz = loot->caps[0].start - src; - size += sz; - dstsub->caps[dstsub->num_captures].start = src; - dstsub->caps[dstsub->num_captures++].end = loot->caps[0].start; - break; - case '\'': - sz = src + src_len - loot->caps[0].end; - size += sz; - dstsub->caps[dstsub->num_captures].start = loot->caps[0].end; - dstsub->caps[dstsub->num_captures++].end = loot->caps[0].end + sz; - break; - case '$': - size++; - dstsub->caps[dstsub->num_captures].start = rstr - 1; - dstsub->caps[dstsub->num_captures++].end = rstr; - break; - default: - return SLRE_BAD_CHAR_AFTER_USD; - } - } else { - char tmps[300], *d = tmps; - size += (sz = runetochar(d, &curr_rune)); - if (!dstsub->num_captures || - dstsub->caps[dstsub->num_captures - 1].end != rstr - sz) { - dstsub->caps[dstsub->num_captures].start = rstr - sz; - dstsub->caps[dstsub->num_captures++].end = rstr; - } else - dstsub->caps[dstsub->num_captures - 1].end = rstr; - } - } - return size; -} - -int slre_match(const char *re, size_t re_len, const char *flags, size_t fl_len, - const char *str, size_t str_len, struct slre_loot *loot) { - struct slre_prog *prog = NULL; - int res; - - if ((res = slre_compile(re, re_len, flags, fl_len, &prog, 1)) == SLRE_OK) { - res = slre_exec(prog, prog->flags & SLRE_FLAG_G, str, str + str_len, loot); - slre_free(prog); - } - - return res; -} - -int slre_get_flags(struct slre_prog *crp) { - return crp->flags; -} - -#ifdef SLRE_TEST - -#include <errno.h> - -static const char *err_code_to_str(int err_code) { - static const char *ar[] = { - "no error", "invalid decimal digit", "invalid hex digit", - "invalid escape character", "invalid unterminated escape sequence", - "syntax error", "unmatched left parenthesis", - "unmatched right parenthesis", "numeric overflow", - "infinite loop empty string", "too many charsets", - "invalid charset range", "charset is too large", "malformed charset", - "invalid back reference", "too many captures", "invalid quantifier", - "bad character after $"}; - - typedef char static_assertion_err_codes_out_of_sync - [2 * !!(((sizeof(ar) / sizeof(ar[0])) == SLRE_BAD_CHAR_AFTER_USD + 1)) - - 1]; - - return err_code >= 0 && err_code < (int) (sizeof(ar) / sizeof(ar[0])) - ? ar[err_code] - : "invalid error code"; -} - -#define RE_TEST_STR_SIZE 2000 - -static unsigned get_flags(const char *ch) { - unsigned int flags = 0; - - while (*ch != '\0') { - switch (*ch) { - case 'g': - flags |= SLRE_FLAG_G; - break; - case 'i': - flags |= SLRE_FLAG_I; - break; - case 'm': - flags |= SLRE_FLAG_M; - break; - case 'r': - flags |= SLRE_FLAG_RE; - break; - default: - return flags; - } - ch++; - } - return flags; -} - -static void show_usage_and_exit(char *argv[]) { - fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]); - fprintf(stderr, "%s\n", "OPTIONS:"); - fprintf(stderr, "%s\n", " -p <regex_pattern> Regex pattern"); - fprintf(stderr, "%s\n", " -o <regex_flags> Combination of g,i,m"); - fprintf(stderr, "%s\n", " -s <string> String to match"); - fprintf(stderr, "%s\n", " -f <file_name> Match lines from file"); - fprintf(stderr, "%s\n", " -n <cap_no> Show given capture"); - fprintf(stderr, "%s\n", " -r <replace_str> Replace given capture"); - fprintf(stderr, "%s\n", " -v Show verbose stats"); - exit(1); -} - -static int process_line(struct slre_prog *pr, const char *flags, - const char *line, const char *cap_no, - const char *replace, const char *verbose) { - struct slre_loot loot; - unsigned int fl = flags == NULL ? 0 : get_flags(flags); - int i, n = cap_no == NULL ? -1 : atoi(cap_no), err_code = 0; - struct slre_cap *cap = &loot.caps[n]; - - err_code = - slre_exec(pr, pr->flags & SLRE_FLAG_G, line, line + strlen(line), &loot); - if (err_code == SLRE_OK) { - if (n >= 0 && n < loot.num_captures && replace != NULL) { - struct slre_cap *cap = &loot.caps[n]; - printf("%.*s", (int) (cap->start - line), line); - printf("%s", replace); - printf("%.*s", (int) ((line + strlen(line)) - cap->end), cap->end); - } else if (n >= 0 && n < loot.num_captures) { - printf("%.*s\n", (int) (cap->end - cap->start), cap->start); - } - - if (verbose != NULL) { - fprintf(stderr, "%s\n", "Captures:"); - for (i = 0; i < loot.num_captures; i++) { - fprintf(stderr, "%d [%.*s]\n", i, - (int) (loot.caps[i].end - loot.caps[i].start), - loot.caps[i].start); - } - } - } - - return err_code; -} - -int main(int argc, char **argv) { - const char *str = NULL, *pattern = NULL, *replace = NULL; - const char *flags = "", *file_name = NULL, *cap_no = NULL, *verbose = NULL; - struct slre_prog *pr = NULL; - int i, err_code = 0; - - /* Execute inline code */ - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) { - pattern = argv[++i]; - } else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) { - flags = argv[++i]; - } else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) { - str = argv[++i]; - } else if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) { - file_name = argv[++i]; - } else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) { - cap_no = argv[++i]; - } else if (strcmp(argv[i], "-r") == 0 && i + 1 < argc) { - replace = argv[++i]; - } else if (strcmp(argv[i], "-v") == 0) { - verbose = ""; - } else if (strcmp(argv[i], "-h") == 0) { - show_usage_and_exit(argv); - } else { - show_usage_and_exit(argv); - } - } - - if (pattern == NULL) { - fprintf(stderr, "%s\n", "-p option is mandatory"); - exit(1); - } else if ((err_code = slre_compile(pattern, strlen(pattern), flags, - strlen(flags), &pr, 1)) != SLRE_OK) { - fprintf(stderr, "slre_compile(%s): %s\n", argv[0], - err_code_to_str(err_code)); - exit(1); - } else if (str != NULL) { - err_code = process_line(pr, flags, str, cap_no, replace, verbose); - } else if (file_name != NULL) { - FILE *fp = strcmp(file_name, "-") == 0 ? stdin : fopen(file_name, "rb"); - char line[20 * 1024]; - if (fp == NULL) { - fprintf(stderr, "Cannot open %s: %s\n", file_name, strerror(errno)); - exit(1); - } else { - /* Return success if at least one line matches */ - err_code = 1; - while (fgets(line, sizeof(line), fp) != NULL) { - if (process_line(pr, flags, line, cap_no, replace, verbose) == - SLRE_OK) { - err_code = 0; - } - } - fclose(fp); /* If fp == stdin, it is safe to close, too */ - } - } else { - fprintf(stderr, "%s\n", "Please specify one of -s or -f options"); - exit(1); - } - slre_free(pr); - - return err_code; -} -#endif /* SLRE_TEST */ - -#endif /* V7_ENABLE__RegExp */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/heapusage.c" -#endif -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#include <stdlib.h> -#include <stdio.h> -#include <assert.h> - -#if defined(V7_HEAPUSAGE_ENABLE) - -/* - * A flag that is set by GC before allocating its buffers, so we can - * distinguish these buffers from other allocations - */ -volatile int heap_dont_count = 0; - -extern void *__real_malloc(size_t size); -extern void *__real_calloc(size_t num, size_t size); -extern void *__real_realloc(void *p, size_t size); -extern void __real_free(void *p); - -/* TODO(dfrank): make it dynamically allocated from heap */ -#define CELLS_CNT (1024 * 32) - -typedef struct cell { - void *p; - unsigned dont_count : 1; - unsigned size : 31; -} cell_t; - -typedef struct alloc_registry { - size_t used_cells_cnt; - size_t allocated_size; - size_t real_used_cells_cnt; - size_t real_allocated_size; - cell_t cells[CELLS_CNT]; -} alloc_registry_t; - -static alloc_registry_t registry = {0}; - -/* - * Make a record about an allocated buffer `p` of size `size` - */ -static void cell_allocated(void *p, size_t size) { - int i; - int cell_num = -1; - - if (p != NULL && size != 0) { - /* TODO(dfrank): make it dynamically allocated from heap */ - assert(registry.real_used_cells_cnt < CELLS_CNT); - - for (i = 0; i < CELLS_CNT; ++i) { - if (registry.cells[i].p == NULL) { - cell_num = i; - break; - } - } - - assert(cell_num != -1); - - registry.cells[cell_num].p = p; - registry.cells[cell_num].size = size; - registry.cells[cell_num].dont_count = !!heap_dont_count; - - registry.real_allocated_size += size; - registry.real_used_cells_cnt += 1; - - if (!heap_dont_count) { - registry.allocated_size += size; - registry.used_cells_cnt += 1; - } - -#if 0 - printf("alloc=0x%lx, size=%lu, total=%lu\n", (unsigned long)p, size, - registry.allocated_size); -#endif - } -} - -/* - * Delete a record about an allocated buffer `p`. If our registry does not - * contain anything about the given pointer, the call is ignored. We can't - * generate an error because shared libraries still use unwrapped heap - * functions, so we can face "unknown" pointers. - */ -static void cell_freed(void *p) { - int i; - int cell_num = -1; - - if (p != NULL) { - assert(registry.real_used_cells_cnt > 0); - - for (i = 0; i < CELLS_CNT; ++i) { - if (registry.cells[i].p == p) { - cell_num = i; - break; - } - } - - /* - * NOTE: it would be nice to have `assert(cell_num != -1);`, but - * unfortunately not all allocations are wrapped: shared libraries will - * still use unwrapped mallocs, so we might get unknown pointers here. - */ - - if (cell_num != -1) { - registry.real_allocated_size -= registry.cells[cell_num].size; - registry.real_used_cells_cnt -= 1; - - if (!registry.cells[cell_num].dont_count) { - registry.allocated_size -= registry.cells[cell_num].size; - registry.used_cells_cnt -= 1; - } - - registry.cells[cell_num].p = NULL; - registry.cells[cell_num].size = 0; - registry.cells[cell_num].dont_count = 0; - -#if 0 - printf("free=0x%lx, total=%lu\n", (unsigned long)p, registry.allocated_size); -#endif - } - } -} - -/* - * Wrappers of the standard heap functions - */ - -void *__wrap_malloc(size_t size) { - void *ret = __real_malloc(size); - cell_allocated(ret, size); - return ret; -} - -void *__wrap_calloc(size_t num, size_t size) { - void *ret = __real_calloc(num, size); - cell_allocated(ret, num * size); - return ret; -} - -void *__wrap_realloc(void *p, size_t size) { - void *ret; - cell_freed(p); - ret = __real_realloc(p, size); - cell_allocated(ret, size); - return ret; -} - -void __wrap_free(void *p) { - __real_free(p); - cell_freed(p); -} - -/* - * Small API to get some stats, see header file for details - */ - -size_t heapusage_alloc_size(void) { - return registry.allocated_size; -} - -size_t heapusage_allocs_cnt(void) { - return registry.used_cells_cnt; -} - -#endif /* V7_HEAPUSAGE_ENABLE */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/cyg_profile.c" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* - * This file contains GCC/clang instrumentation callbacks. The actual - * code in these callbacks depends on enabled features. - * - * Currently, the code from different subsystems is embedded right into - * callbacks for performance reasons. It would be probably more elegant - * to have subsystem-specific functions that will be called from these - * callbacks, but since the callbacks are called really a lot (on each v7 - * function call), I decided it's better to inline the code right here. - */ - -/* Amalgamated: #include "v7/src/internal.h" */ -/* Amalgamated: #include "v7/src/cyg_profile.h" */ -/* Amalgamated: #include "v7/src/core.h" */ - -#if defined(V7_CYG_PROFILE_ON) - -#if defined(V7_ENABLE_CALL_TRACE) - -#define CALL_TRACE_SIZE 32 - -typedef struct { - uint16_t size; - uint16_t missed_cnt; - void *addresses[CALL_TRACE_SIZE]; -} call_trace_t; - -static call_trace_t call_trace = {0}; - -NOINSTR -void call_trace_print(const char *prefix, const char *suffix, size_t skip_cnt, - size_t max_cnt) { - int i; - if (call_trace.missed_cnt > 0) { - fprintf(stderr, "missed calls! (%d) ", (int) call_trace.missed_cnt); - } - if (prefix != NULL) { - fprintf(stderr, "%s", prefix); - } - for (i = (int) call_trace.size - 1 - skip_cnt; i >= 0; i--) { - fprintf(stderr, " %lx", (unsigned long) call_trace.addresses[i]); - if (max_cnt > 0) { - if (--max_cnt == 0) { - break; - } - } - } - if (suffix != NULL) { - fprintf(stderr, "%s", suffix); - } - fprintf(stderr, "\n"); -} - -#endif - -#ifndef IRAM -#define IRAM -#endif - -#ifndef NOINSTR -#define NOINSTR __attribute__((no_instrument_function)) -#endif - -#if defined(__cplusplus) -extern "C" { -#endif /* __cplusplus */ -IRAM NOINSTR void __cyg_profile_func_enter(void *this_fn, void *call_site); - -IRAM NOINSTR void __cyg_profile_func_exit(void *this_fn, void *call_site); - -#if defined(__cplusplus) -} -#endif /* __cplusplus */ - -IRAM void __cyg_profile_func_enter(void *this_fn, void *call_site) { -#if defined(V7_STACK_GUARD_MIN_SIZE) - { - static int profile_enter = 0; - void *fp = __builtin_frame_address(0); - - (void) call_site; - - if (profile_enter || v7_sp_limit == NULL) return; - - profile_enter++; - if (v7_head != NULL && fp < v7_head->sp_lwm) v7_head->sp_lwm = fp; - - if (((int) fp - (int) v7_sp_limit) < V7_STACK_GUARD_MIN_SIZE) { - printf("fun %p sp %p limit %p left %d\n", this_fn, fp, v7_sp_limit, - (int) fp - (int) v7_sp_limit); - abort(); - } - profile_enter--; - } -#endif - -#if defined(V7_ENABLE_GC_CHECK) - { - (void) this_fn; - (void) call_site; - } -#endif - -#if defined(V7_ENABLE_STACK_TRACKING) - { - struct v7 *v7; - struct stack_track_ctx *ctx; - void *fp = __builtin_frame_address(1); - - (void) this_fn; - (void) call_site; - - /* - * TODO(dfrank): it actually won't work for multiple instances of v7 running - * in parallel threads. We need to know the exact v7 instance for which - * current function is called, but so far I failed to find a way to do this. - */ - for (v7 = v7_head; v7 != NULL; v7 = v7->next_v7) { - for (ctx = v7->stack_track_ctx; ctx != NULL; ctx = ctx->next) { - /* commented because it fails on legal code compiled with -O3 */ - /*assert(fp <= ctx->start);*/ - - if (fp < ctx->max) { - ctx->max = fp; - } - } - } - } -#endif - -#if defined(V7_ENABLE_CALL_TRACE) - if (call_trace.size < CALL_TRACE_SIZE) { - call_trace.addresses[call_trace.size] = this_fn; - call_trace.size++; - } else { - call_trace.missed_cnt++; - } -#endif -} - -IRAM void __cyg_profile_func_exit(void *this_fn, void *call_site) { -#if defined(V7_STACK_GUARD_MIN_SIZE) - { - (void) this_fn; - (void) call_site; - } -#endif - -#if defined(V7_ENABLE_GC_CHECK) - { - struct v7 *v7; - void *fp = __builtin_frame_address(1); - - (void) this_fn; - (void) call_site; - - for (v7 = v7_head; v7 != NULL; v7 = v7->next_v7) { - v7_val_t **vp; - if (v7->owned_values.buf == NULL) continue; - vp = (v7_val_t **) (v7->owned_values.buf + v7->owned_values.len - - sizeof(v7_val_t *)); - - for (; (char *) vp >= v7->owned_values.buf; vp--) { - /* - * Check if a variable belongs to a dead stack frame. - * Addresses lower than the parent frame belong to the - * stack frame of the function about to return. - * But the heap also usually below the stack and - * we don't know the end of the stack. But this hook - * is called at each function return, so we have - * to check only up to the maximum stack frame size, - * let's arbitrarily but reasonably set that at 8k. - */ - if ((void *) *vp <= fp && (void *) *vp > (fp + 8196)) { - fprintf(stderr, "Found owned variable after return\n"); - abort(); - } - } - } - } -#endif - -#if defined(V7_ENABLE_STACK_TRACKING) - { - (void) this_fn; - (void) call_site; - } -#endif - -#if defined(V7_ENABLE_CALL_TRACE) - if (call_trace.missed_cnt > 0) { - call_trace.missed_cnt--; - } else if (call_trace.size > 0) { - if (call_trace.addresses[call_trace.size - 1] != this_fn) { - abort(); - } - call_trace.size--; - } else { - /* - * We may get here if calls to `__cyg_profile_func_exit()` and - * `__cyg_profile_func_enter()` are unbalanced. - * - * TODO(dfrank) understand, why in the beginning of the program execution - * we get here. I was sure this should be impossible. - */ - /* abort(); */ - } -#endif -} - -#if defined(V7_ENABLE_STACK_TRACKING) - -void v7_stack_track_start(struct v7 *v7, struct stack_track_ctx *ctx) { - /* insert new context at the head of the list */ - ctx->next = v7->stack_track_ctx; - v7->stack_track_ctx = ctx; - - /* init both `max` and `start` to the current frame pointer */ - ctx->max = ctx->start = __builtin_frame_address(0); -} - -int v7_stack_track_end(struct v7 *v7, struct stack_track_ctx *ctx) { - int diff; - - /* this function can be called only for the head context */ - assert(v7->stack_track_ctx == ctx); - - diff = (int) ((char *) ctx->start - (char *) ctx->max); - - /* remove context from the linked list */ - v7->stack_track_ctx = ctx->next; - - return (int) diff; -} - -#endif /* V7_ENABLE_STACK_TRACKING */ -#endif /* V7_CYG_PROFILE_ON */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_object.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/std_object.h" */ -/* Amalgamated: #include "v7/src/function.h" */ -/* Amalgamated: #include "v7/src/core.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "v7/src/array.h" */ -/* Amalgamated: #include "v7/src/object.h" */ -/* Amalgamated: #include "v7/src/exceptions.h" */ -/* Amalgamated: #include "v7/src/primitive.h" */ -/* Amalgamated: #include "v7/src/string.h" */ -/* Amalgamated: #include "v7/src/regexp.h" */ -/* Amalgamated: #include "v7/src/exec.h" */ - -#if V7_ENABLE__Object__getPrototypeOf -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_getPrototypeOf(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t arg = v7_arg(v7, 0); - - if (!v7_is_object(arg)) { - rcode = - v7_throwf(v7, TYPE_ERROR, "Object.getPrototypeOf called on non-object"); - goto clean; - } - *res = v7_get_proto(v7, arg); - -clean: - return rcode; -} -#endif - -#if V7_ENABLE__Object__isPrototypeOf -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_isPrototypeOf(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t obj = v7_arg(v7, 0); - val_t proto = v7_get_this(v7); - - *res = v7_mk_boolean(v7, is_prototype_of(v7, obj, proto)); - - return rcode; -} -#endif - -#if V7_ENABLE__Object__getOwnPropertyNames || V7_ENABLE__Object__keys -/* - * Hack to ensure that the iteration order of the keys array is consistent - * with the iteration order if properties in `for in` - * This will be obsoleted when arrays will have a special object type. - */ -static void _Obj_append_reverse(struct v7 *v7, struct v7_property *p, val_t res, - int i, v7_prop_attr_t ignore_flags) { - while (p && p->attributes & ignore_flags) p = p->next; - if (p == NULL) return; - if (p->next) _Obj_append_reverse(v7, p->next, res, i + 1, ignore_flags); - - v7_array_set(v7, res, i, p->name); -} - -WARN_UNUSED_RESULT -static enum v7_err _Obj_ownKeys(struct v7 *v7, unsigned int ignore_flags, - val_t *res) { - enum v7_err rcode = V7_OK; - val_t obj = v7_arg(v7, 0); - - *res = v7_mk_dense_array(v7); - - if (!v7_is_object(obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Object.keys called on non-object"); - goto clean; - } - - _Obj_append_reverse(v7, get_object_struct(obj)->properties, *res, 0, - ignore_flags); - -clean: - return rcode; -} -#endif - -#if V7_ENABLE__Object__hasOwnProperty || \ - V7_ENABLE__Object__propertyIsEnumerable || \ - V7_ENABLE__Object__getOwnPropertyDescriptor -static enum v7_err _Obj_getOwnProperty(struct v7 *v7, val_t obj, val_t name, - struct v7_property **res) { - enum v7_err rcode = V7_OK; - char name_buf[512]; - size_t name_len; - - rcode = to_string(v7, name, NULL, name_buf, sizeof(name_buf), &name_len); - if (rcode != V7_OK) { - goto clean; - } - - *res = v7_get_own_property(v7, obj, name_buf, name_len); - -clean: - return rcode; -} -#endif - -#if V7_ENABLE__Object__keys -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_keys(struct v7 *v7, v7_val_t *res) { - return _Obj_ownKeys(v7, _V7_PROPERTY_HIDDEN | V7_PROPERTY_NON_ENUMERABLE, - res); -} -#endif - -#if V7_ENABLE__Object__getOwnPropertyNames -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_getOwnPropertyNames(struct v7 *v7, v7_val_t *res) { - return _Obj_ownKeys(v7, _V7_PROPERTY_HIDDEN, res); -} -#endif - -#if V7_ENABLE__Object__getOwnPropertyDescriptor -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_getOwnPropertyDescriptor(struct v7 *v7, - v7_val_t *res) { - enum v7_err rcode = V7_OK; - struct v7_property *prop; - val_t obj = v7_arg(v7, 0); - val_t name = v7_arg(v7, 1); - val_t desc; - - rcode = _Obj_getOwnProperty(v7, obj, name, &prop); - if (rcode != V7_OK) { - goto clean; - } - - if (prop == NULL) { - goto clean; - } - - desc = v7_mk_object(v7); - v7_set(v7, desc, "value", 5, prop->value); - v7_set(v7, desc, "writable", 8, - v7_mk_boolean(v7, !(prop->attributes & V7_PROPERTY_NON_WRITABLE))); - v7_set(v7, desc, "enumerable", 10, - v7_mk_boolean(v7, !(prop->attributes & (_V7_PROPERTY_HIDDEN | - V7_PROPERTY_NON_ENUMERABLE)))); - v7_set(v7, desc, "configurable", 12, - v7_mk_boolean(v7, !(prop->attributes & V7_PROPERTY_NON_CONFIGURABLE))); - - *res = desc; - -clean: - return rcode; -} -#endif - -WARN_UNUSED_RESULT -static enum v7_err o_set_attr(struct v7 *v7, val_t desc, const char *name, - size_t n, v7_prop_attr_desc_t *pattrs_delta, - v7_prop_attr_desc_t flag_true, - v7_prop_attr_desc_t flag_false) { - enum v7_err rcode = V7_OK; - - val_t v = V7_UNDEFINED; - rcode = v7_get_throwing(v7, desc, name, n, &v); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_is_truthy(v7, v)) { - *pattrs_delta |= flag_true; - } else { - *pattrs_delta |= flag_false; - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -static enum v7_err _Obj_defineProperty(struct v7 *v7, val_t obj, - const char *name, int name_len, - val_t desc, val_t *res) { - enum v7_err rcode = V7_OK; - val_t val = V7_UNDEFINED; - v7_prop_attr_desc_t attrs_desc = 0; - - /* - * get provided value, or set `V7_DESC_PRESERVE_VALUE` flag if no value is - * provided at all - */ - { - struct v7_property *prop = v7_get_property(v7, desc, "value", 5); - if (prop == NULL) { - /* no value is provided */ - attrs_desc |= V7_DESC_PRESERVE_VALUE; - } else { - /* value is provided: use it */ - rcode = v7_property_value(v7, desc, prop, &val); - if (rcode != V7_OK) { - goto clean; - } - } - } - - /* Examine given properties, and set appropriate flags for `def_property` */ - - rcode = o_set_attr(v7, desc, "enumerable", 10, &attrs_desc, - V7_DESC_ENUMERABLE(1), V7_DESC_ENUMERABLE(0)); - if (rcode != V7_OK) { - goto clean; - } - - rcode = o_set_attr(v7, desc, "writable", 8, &attrs_desc, V7_DESC_WRITABLE(1), - V7_DESC_WRITABLE(0)); - if (rcode != V7_OK) { - goto clean; - } - - rcode = o_set_attr(v7, desc, "configurable", 12, &attrs_desc, - V7_DESC_CONFIGURABLE(1), V7_DESC_CONFIGURABLE(0)); - if (rcode != V7_OK) { - goto clean; - } - - /* TODO(dfrank) : add getter/setter support */ - - /* Finally, do the job on defining the property */ - rcode = def_property(v7, obj, name, name_len, attrs_desc, val, - 0 /*not assign*/, NULL); - if (rcode != V7_OK) { - goto clean; - } - - *res = obj; - goto clean; - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_defineProperty(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t obj = v7_arg(v7, 0); - val_t name = v7_arg(v7, 1); - val_t desc = v7_arg(v7, 2); - char name_buf[512]; - size_t name_len; - - if (!v7_is_object(obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "object expected"); - goto clean; - } - - rcode = to_string(v7, name, NULL, name_buf, sizeof(name_buf), &name_len); - if (rcode != V7_OK) { - goto clean; - } - - rcode = _Obj_defineProperty(v7, obj, name_buf, name_len, desc, res); - goto clean; - -clean: - return rcode; -} - -#if V7_ENABLE__Object__create || V7_ENABLE__Object__defineProperties -WARN_UNUSED_RESULT -static enum v7_err o_define_props(struct v7 *v7, val_t obj, val_t descs, - val_t *res) { - enum v7_err rcode = V7_OK; - struct v7_property *p; - - if (!v7_is_object(descs)) { - rcode = v7_throwf(v7, TYPE_ERROR, "object expected"); - goto clean; - } - - for (p = get_object_struct(descs)->properties; p; p = p->next) { - size_t n; - const char *s = v7_get_string(v7, &p->name, &n); - if (p->attributes & (_V7_PROPERTY_HIDDEN | V7_PROPERTY_NON_ENUMERABLE)) { - continue; - } - rcode = _Obj_defineProperty(v7, obj, s, n, p->value, res); - if (rcode != V7_OK) { - goto clean; - } - } - -clean: - return rcode; -} -#endif - -#if V7_ENABLE__Object__defineProperties -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_defineProperties(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t descs = V7_UNDEFINED; - - *res = v7_arg(v7, 0); - descs = v7_arg(v7, 1); - rcode = o_define_props(v7, *res, descs, res); - if (rcode != V7_OK) { - goto clean; - } - -clean: - return rcode; -} -#endif - -#if V7_ENABLE__Object__create -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_create(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t proto = v7_arg(v7, 0); - val_t descs = v7_arg(v7, 1); - if (!v7_is_null(proto) && !v7_is_object(proto)) { - rcode = v7_throwf(v7, TYPE_ERROR, - "Object prototype may only be an Object or null"); - goto clean; - } - *res = mk_object(v7, proto); - if (v7_is_object(descs)) { - rcode = o_define_props(v7, *res, descs, res); - if (rcode != V7_OK) { - goto clean; - } - } - -clean: - return rcode; -} -#endif - -#if V7_ENABLE__Object__propertyIsEnumerable -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_propertyIsEnumerable(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - struct v7_property *prop; - val_t name = v7_arg(v7, 0); - - rcode = _Obj_getOwnProperty(v7, this_obj, name, &prop); - if (rcode != V7_OK) { - goto clean; - } - - if (prop == NULL) { - *res = v7_mk_boolean(v7, 0); - } else { - *res = - v7_mk_boolean(v7, !(prop->attributes & (_V7_PROPERTY_HIDDEN | - V7_PROPERTY_NON_ENUMERABLE))); - } - - goto clean; - -clean: - return rcode; -} -#endif - -#if V7_ENABLE__Object__hasOwnProperty -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_hasOwnProperty(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t name = v7_arg(v7, 0); - struct v7_property *ptmp = NULL; - - rcode = _Obj_getOwnProperty(v7, this_obj, name, &ptmp); - if (rcode != V7_OK) { - goto clean; - } - - *res = v7_mk_boolean(v7, ptmp != NULL); - goto clean; - -clean: - return rcode; -} -#endif - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_valueOf(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - struct v7_property *p; - - *res = this_obj; - - if (v7_is_regexp(v7, this_obj)) { - /* res is `this_obj` */ - goto clean; - } - - p = v7_get_own_property2(v7, this_obj, "", 0, _V7_PROPERTY_HIDDEN); - if (p != NULL) { - *res = p->value; - goto clean; - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_toString(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t ctor, name, this_obj = v7_get_this(v7); - char buf[20]; - const char *str = "Object"; - size_t name_len = ~0; - - if (v7_is_undefined(this_obj)) { - str = "Undefined"; - } else if (v7_is_null(this_obj)) { - str = "Null"; - } else if (v7_is_number(this_obj)) { - str = "Number"; - } else if (v7_is_boolean(this_obj)) { - str = "Boolean"; - } else if (v7_is_string(this_obj)) { - str = "String"; - } else if (v7_is_callable(v7, this_obj)) { - str = "Function"; - } else { - rcode = v7_get_throwing(v7, this_obj, "constructor", ~0, &ctor); - if (rcode != V7_OK) { - goto clean; - } - - if (!v7_is_undefined(ctor)) { - rcode = v7_get_throwing(v7, ctor, "name", ~0, &name); - if (rcode != V7_OK) { - goto clean; - } - - if (!v7_is_undefined(name)) { - size_t tmp_len; - const char *tmp_str; - tmp_str = v7_get_string(v7, &name, &tmp_len); - /* - * objects constructed with an anonymous constructor are represented as - * Object, ch11/11.1/11.1.1/S11.1.1_A4.2.js - */ - if (tmp_len > 0) { - str = tmp_str; - name_len = tmp_len; - } - } - } - } - - if (name_len == (size_t) ~0) { - name_len = strlen(str); - } - - c_snprintf(buf, sizeof(buf), "[object %.*s]", (int) name_len, str); - *res = v7_mk_string(v7, buf, strlen(buf), 1); - -clean: - return rcode; -} - -#if V7_ENABLE__Object__preventExtensions -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_preventExtensions(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t arg = v7_arg(v7, 0); - if (!v7_is_object(arg)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Object expected"); - goto clean; - } - get_object_struct(arg)->attributes |= V7_OBJ_NOT_EXTENSIBLE; - *res = arg; - -clean: - return rcode; -} -#endif - -#if V7_ENABLE__Object__isExtensible -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_isExtensible(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t arg = v7_arg(v7, 0); - - if (!v7_is_object(arg)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Object expected"); - goto clean; - } - - *res = v7_mk_boolean( - v7, !(get_object_struct(arg)->attributes & V7_OBJ_NOT_EXTENSIBLE)); - -clean: - return rcode; -} -#endif - -#if V7_ENABLE__Object__isFrozen || V7_ENABLE__Object__isSealed -static enum v7_err is_rigid(struct v7 *v7, v7_val_t *res, int is_frozen) { - enum v7_err rcode = V7_OK; - int ok = 0; - val_t arg = v7_arg(v7, 0); - - if (!v7_is_object(arg)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Object expected"); - goto clean; - } - - *res = v7_mk_boolean(v7, 0); - - if (get_object_struct(arg)->attributes & V7_OBJ_NOT_EXTENSIBLE) { - v7_prop_attr_t attrs = 0; - struct prop_iter_ctx ctx; - memset(&ctx, 0, sizeof(ctx)); - V7_TRY2(init_prop_iter_ctx(v7, arg, 1, &ctx), clean_iter); - while (1) { - V7_TRY2(next_prop(v7, &ctx, NULL, NULL, &attrs, &ok), clean_iter); - if (!ok) { - break; - } - if (!(attrs & V7_PROPERTY_NON_CONFIGURABLE)) { - goto clean_iter; - } - if (is_frozen) { - if (!(attrs & V7_PROPERTY_SETTER) && - !(attrs & V7_PROPERTY_NON_WRITABLE)) { - goto clean_iter; - } - } - } - - *res = v7_mk_boolean(v7, 1); - - clean_iter: - v7_destruct_prop_iter_ctx(v7, &ctx); - goto clean; - } - -clean: - return rcode; -} -#endif - -#if V7_ENABLE__Object__isSealed -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_isSealed(struct v7 *v7, v7_val_t *res) { - return is_rigid(v7, res, 0 /* is_frozen */); -} -#endif - -#if V7_ENABLE__Object__isFrozen -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Obj_isFrozen(struct v7 *v7, v7_val_t *res) { - return is_rigid(v7, res, 1 /* is_frozen */); -} -#endif - -static const char js_function_Object[] = - "function Object(v) {" - "if (typeof v === 'boolean') return new Boolean(v);" - "if (typeof v === 'number') return new Number(v);" - "if (typeof v === 'string') return new String(v);" - "if (typeof v === 'date') return new Date(v);" - "}"; - -V7_PRIVATE void init_object(struct v7 *v7) { - enum v7_err rcode = V7_OK; - val_t object, v; - /* TODO(mkm): initialize global object without requiring a parser */ - rcode = v7_exec(v7, js_function_Object, &v); - assert(rcode == V7_OK); -#if defined(NDEBUG) - (void) rcode; -#endif - - object = v7_get(v7, v7->vals.global_object, "Object", 6); - v7_set(v7, object, "prototype", 9, v7->vals.object_prototype); - v7_def(v7, v7->vals.object_prototype, "constructor", 11, - V7_DESC_ENUMERABLE(0), object); - - set_method(v7, v7->vals.object_prototype, "toString", Obj_toString, 0); -#if V7_ENABLE__Object__getPrototypeOf - set_cfunc_prop(v7, object, "getPrototypeOf", Obj_getPrototypeOf); -#endif -#if V7_ENABLE__Object__getOwnPropertyDescriptor - set_cfunc_prop(v7, object, "getOwnPropertyDescriptor", - Obj_getOwnPropertyDescriptor); -#endif - - /* defineProperty is currently required to perform stdlib initialization */ - set_method(v7, object, "defineProperty", Obj_defineProperty, 3); - -#if V7_ENABLE__Object__defineProperties - set_cfunc_prop(v7, object, "defineProperties", Obj_defineProperties); -#endif -#if V7_ENABLE__Object__create - set_cfunc_prop(v7, object, "create", Obj_create); -#endif -#if V7_ENABLE__Object__keys - set_cfunc_prop(v7, object, "keys", Obj_keys); -#endif -#if V7_ENABLE__Object__getOwnPropertyNames - set_cfunc_prop(v7, object, "getOwnPropertyNames", Obj_getOwnPropertyNames); -#endif -#if V7_ENABLE__Object__preventExtensions - set_method(v7, object, "preventExtensions", Obj_preventExtensions, 1); -#endif -#if V7_ENABLE__Object__isExtensible - set_method(v7, object, "isExtensible", Obj_isExtensible, 1); -#endif -#if V7_ENABLE__Object__isSealed - set_method(v7, object, "isSealed", Obj_isSealed, 1); -#endif -#if V7_ENABLE__Object__isFrozen - set_method(v7, object, "isFrozen", Obj_isFrozen, 1); -#endif - -#if V7_ENABLE__Object__propertyIsEnumerable - set_cfunc_prop(v7, v7->vals.object_prototype, "propertyIsEnumerable", - Obj_propertyIsEnumerable); -#endif -#if V7_ENABLE__Object__hasOwnProperty - set_cfunc_prop(v7, v7->vals.object_prototype, "hasOwnProperty", - Obj_hasOwnProperty); -#endif -#if V7_ENABLE__Object__isPrototypeOf - set_cfunc_prop(v7, v7->vals.object_prototype, "isPrototypeOf", - Obj_isPrototypeOf); -#endif - set_cfunc_prop(v7, v7->vals.object_prototype, "valueOf", Obj_valueOf); -} -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_error.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/function.h" */ -/* Amalgamated: #include "v7/src/string.h" */ -/* Amalgamated: #include "v7/src/std_error.h" */ -/* Amalgamated: #include "v7/src/object.h" */ -/* Amalgamated: #include "v7/src/bcode.h" */ -/* Amalgamated: #include "v7/src/primitive.h" */ -/* Amalgamated: #include "v7/src/util.h" */ - -/* - * TODO(dfrank): make the top of v7->call_frame to represent the current - * frame, and thus get rid of the `CUR_LINENO()` - */ -#ifndef V7_DISABLE_LINE_NUMBERS -#define CALLFRAME_LINENO(call_frame) ((call_frame)->line_no) -#define CUR_LINENO() (v7->line_no) -#else -#define CALLFRAME_LINENO(call_frame) 0 -#define CUR_LINENO() 0 -#endif - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Error_ctor(struct v7 *v7, v7_val_t *res); - -#if !defined(V7_DISABLE_FILENAMES) && !defined(V7_DISABLE_LINE_NUMBERS) -static int printf_stack_line(char *p, size_t len, struct bcode *bcode, - int line_no, const char *leading) { - int ret; - const char *fn = bcode_get_filename(bcode); - if (fn == NULL) { - fn = "<no filename>"; - } - - if (bcode->func_name_present) { - /* this is a function's bcode: let's show the function's name as well */ - char *funcname; - - /* - * read first name from the bcode ops, which is the function name, - * since `func_name_present` is set - */ - bcode_next_name(bcode->ops.p, &funcname, NULL); - - /* Check if it's an anonymous function */ - if (funcname[0] == '\0') { - funcname = (char *) "<anonymous>"; - } - ret = - snprintf(p, len, "%s at %s (%s:%d)", leading, funcname, fn, line_no); - } else { - /* it's a file's bcode: show only filename and line number */ - ret = snprintf(p, len, "%s at %s:%d", leading, fn, line_no); - } - return ret; -} - -static int printf_stack_line_cfunc(char *p, size_t len, v7_cfunction_t *cfunc, - const char *leading) { - int ret = 0; - -#if !defined(V7_FILENAMES_SUPPRESS_CFUNC_ADDR) - int name_len = - snprintf(NULL, 0, "cfunc_%p", (void *) cfunc) + 1 /*null-term*/; - char *buf = (char *) malloc(name_len); - - snprintf(buf, name_len, "cfunc_%p", (void *) cfunc); -#else - /* - * We need this mode only for ecma test reporting, so that the - * report is not different from one run to another - */ - char *buf = (char *) "cfunc"; - (void) cfunc; -#endif - - ret = snprintf(p, len, "%s at %s", leading, buf); - -#if !defined(V7_FILENAMES_SUPPRESS_CFUNC_ADDR) - free(buf); -#endif - - return ret; -} - -static int print_stack_trace(char *p, size_t len, - struct v7_call_frame_base *call_frame) { - char *p_cur = p; - int total_len = 0; - - assert(call_frame->type_mask == V7_CALL_FRAME_MASK_CFUNC && - ((struct v7_call_frame_cfunc *) call_frame)->cfunc == Error_ctor); - call_frame = call_frame->prev; - - while (call_frame != NULL) { - int cur_len = 0; - const char *leading = (total_len ? "\n" : ""); - size_t line_len = len - (p_cur - p); - - if (call_frame->type_mask & V7_CALL_FRAME_MASK_BCODE) { - struct bcode *bcode = ((struct v7_call_frame_bcode *) call_frame)->bcode; - if (bcode != NULL) { - cur_len = printf_stack_line(p_cur, line_len, bcode, - CALLFRAME_LINENO(call_frame), leading); - } - } else if (call_frame->type_mask & V7_CALL_FRAME_MASK_CFUNC) { - cur_len = printf_stack_line_cfunc( - p_cur, line_len, ((struct v7_call_frame_cfunc *) call_frame)->cfunc, - leading); - } - - total_len += cur_len; - if (p_cur != NULL) { - p_cur += cur_len; - } - - call_frame = call_frame->prev; - -#if !(V7_ENABLE__StackTrace) - break; -#endif - } - - return total_len; -} -#endif - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Error_ctor(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t arg0 = v7_arg(v7, 0); - - if (v7_is_object(this_obj) && this_obj != v7->vals.global_object) { - *res = this_obj; - } else { - *res = mk_object(v7, v7->vals.error_prototype); - } - /* TODO(mkm): set non enumerable but provide toString method */ - v7_set(v7, *res, "message", 7, arg0); - -#if !defined(V7_DISABLE_FILENAMES) && !defined(V7_DISABLE_LINE_NUMBERS) - /* Save the stack trace */ - { - size_t len = 0; - val_t st_v = V7_UNDEFINED; - - v7_own(v7, &st_v); - - len = print_stack_trace(NULL, 0, v7->call_stack); - - if (len > 0) { - /* Now, create a placeholder for string */ - st_v = v7_mk_string(v7, NULL, len, 1); - len += 1 /*null-term*/; - - /* And fill it with actual data */ - print_stack_trace((char *) v7_get_string(v7, &st_v, NULL), len, - v7->call_stack); - - v7_set(v7, *res, "stack", ~0, st_v); - } - - v7_disown(v7, &st_v); - } -#endif - - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Error_toString(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t prefix, msg = v7_get(v7, this_obj, "message", ~0); - - if (!v7_is_string(msg)) { - *res = v7_mk_string(v7, "Error", ~0, 1); - goto clean; - } - - prefix = v7_mk_string(v7, "Error: ", ~0, 1); - *res = s_concat(v7, prefix, msg); - goto clean; - -clean: - return rcode; -} - -static const char *const error_names[] = {TYPE_ERROR, SYNTAX_ERROR, - REFERENCE_ERROR, INTERNAL_ERROR, - RANGE_ERROR, EVAL_ERROR}; - -V7_STATIC_ASSERT(ARRAY_SIZE(error_names) == ERROR_CTOR_MAX, - error_name_count_mismatch); - -V7_PRIVATE void init_error(struct v7 *v7) { - val_t error; - size_t i; - - error = - mk_cfunction_obj_with_proto(v7, Error_ctor, 1, v7->vals.error_prototype); - v7_def(v7, v7->vals.global_object, "Error", 5, V7_DESC_ENUMERABLE(0), error); - set_method(v7, v7->vals.error_prototype, "toString", Error_toString, 0); - - for (i = 0; i < ARRAY_SIZE(error_names); i++) { - error = mk_cfunction_obj_with_proto( - v7, Error_ctor, 1, mk_object(v7, v7->vals.error_prototype)); - v7_def(v7, v7->vals.global_object, error_names[i], strlen(error_names[i]), - V7_DESC_ENUMERABLE(0), error); - v7->vals.error_objects[i] = error; - } -} -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_number.c" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* Amalgamated: #include "v7/src/internal.h" */ -/* Amalgamated: #include "v7/src/std_object.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "v7/src/core.h" */ -/* Amalgamated: #include "v7/src/function.h" */ -/* Amalgamated: #include "v7/src/object.h" */ -/* Amalgamated: #include "v7/src/primitive.h" */ -/* Amalgamated: #include "v7/src/string.h" */ -/* Amalgamated: #include "v7/src/exceptions.h" */ - -#if defined(__cplusplus) -extern "C" { -#endif /* __cplusplus */ - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Number_ctor(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t arg0 = v7_argc(v7) == 0 ? v7_mk_number(v7, 0.0) : v7_arg(v7, 0); - - if (v7_is_number(arg0)) { - *res = arg0; - } else { - rcode = to_number_v(v7, arg0, res); - if (rcode != V7_OK) { - goto clean; - } - } - - if (v7_is_generic_object(this_obj) && this_obj != v7->vals.global_object) { - obj_prototype_set(v7, get_object_struct(this_obj), - get_object_struct(v7->vals.number_prototype)); - v7_def(v7, this_obj, "", 0, _V7_DESC_HIDDEN(1), *res); - - /* - * implicitly returning `this`: `call_cfunction()` in bcode.c will do - * that for us - */ - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err n_to_str(struct v7 *v7, const char *format, val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t arg0 = v7_arg(v7, 0); - int len, digits = 0; - char fmt[10], buf[100]; - - rcode = to_number_v(v7, arg0, &arg0); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_get_double(v7, arg0) > 0) { - digits = (int) v7_get_double(v7, arg0); - } - - /* - * NOTE: we don't own `arg0` and `this_obj`, since this function is called - * from cfunctions only, and GC is inhibited during these calls - */ - - rcode = obj_value_of(v7, this_obj, &this_obj); - if (rcode != V7_OK) { - goto clean; - } - - snprintf(fmt, sizeof(fmt), format, digits); - len = snprintf(buf, sizeof(buf), fmt, v7_get_double(v7, this_obj)); - - *res = v7_mk_string(v7, buf, len, 1); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Number_toFixed(struct v7 *v7, v7_val_t *res) { - return n_to_str(v7, "%%.%dlf", res); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Number_toExp(struct v7 *v7, v7_val_t *res) { - return n_to_str(v7, "%%.%de", res); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Number_toPrecision(struct v7 *v7, v7_val_t *res) { - return Number_toExp(v7, res); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Number_valueOf(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - - if (!v7_is_number(this_obj) && - (v7_is_object(this_obj) && - v7_get_proto(v7, this_obj) != v7->vals.number_prototype)) { - rcode = - v7_throwf(v7, TYPE_ERROR, "Number.valueOf called on non-number object"); - goto clean; - } - - rcode = Obj_valueOf(v7, res); - if (rcode != V7_OK) { - goto clean; - } - -clean: - return rcode; -} - -/* - * Converts a 64 bit signed integer into a string of a given base. - * Requires space for 65 bytes (64 bit + null terminator) in the result buffer - */ -static char *cs_itoa(int64_t value, char *result, int base) { - char *ptr = result, *ptr1 = result, tmp_char; - int64_t tmp_value; - int64_t sign = value < 0 ? -1 : 1; - const char *base36 = "0123456789abcdefghijklmnopqrstuvwxyz"; - - if (base < 2 || base > 36) { - *result = '\0'; - return result; - } - - /* let's think positive */ - value = value * sign; - do { - tmp_value = value; - value /= base; - *ptr++ = base36[tmp_value - value * base]; - } while (value); - - /* sign */ - if (sign < 0) *ptr++ = '-'; - *ptr-- = '\0'; - while (ptr1 < ptr) { - tmp_char = *ptr; - *ptr-- = *ptr1; - *ptr1++ = tmp_char; - } - return result; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Number_toString(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t radixv = v7_arg(v7, 0); - char buf[65]; - double d, radix; - - if (this_obj == v7->vals.number_prototype) { - *res = v7_mk_string(v7, "0", 1, 1); - goto clean; - } - - /* Make sure this function was called on Number instance */ - if (!v7_is_number(this_obj) && - !(v7_is_generic_object(this_obj) && - is_prototype_of(v7, this_obj, v7->vals.number_prototype))) { - rcode = v7_throwf(v7, TYPE_ERROR, - "Number.toString called on non-number object"); - goto clean; - } - - /* Get number primitive */ - rcode = to_number_v(v7, this_obj, &this_obj); - if (rcode != V7_OK) { - goto clean; - } - - /* Get radix if provided, or 10 otherwise */ - if (!v7_is_undefined(radixv)) { - rcode = to_number_v(v7, radixv, &radixv); - if (rcode != V7_OK) { - goto clean; - } - radix = v7_get_double(v7, radixv); - } else { - radix = 10.0; - } - - d = v7_get_double(v7, this_obj); - if (!isnan(d) && (int64_t) d == d && radix >= 2) { - cs_itoa(d, buf, radix); - *res = v7_mk_string(v7, buf, strlen(buf), 1); - } else { - rcode = to_string(v7, this_obj, res, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err n_isNaN(struct v7 *v7, v7_val_t *res) { - val_t arg0 = v7_arg(v7, 0); - *res = v7_mk_boolean(v7, !v7_is_number(arg0) || arg0 == V7_TAG_NAN); - return V7_OK; -} - -V7_PRIVATE void init_number(struct v7 *v7) { - v7_prop_attr_desc_t attrs_desc = - (V7_DESC_WRITABLE(0) | V7_DESC_ENUMERABLE(0) | V7_DESC_CONFIGURABLE(0)); - val_t num = mk_cfunction_obj_with_proto(v7, Number_ctor, 1, - v7->vals.number_prototype); - - v7_def(v7, v7->vals.global_object, "Number", 6, V7_DESC_ENUMERABLE(0), num); - - set_cfunc_prop(v7, v7->vals.number_prototype, "toFixed", Number_toFixed); - set_cfunc_prop(v7, v7->vals.number_prototype, "toPrecision", - Number_toPrecision); - set_cfunc_prop(v7, v7->vals.number_prototype, "toExponential", Number_toExp); - set_cfunc_prop(v7, v7->vals.number_prototype, "valueOf", Number_valueOf); - set_cfunc_prop(v7, v7->vals.number_prototype, "toString", Number_toString); - - v7_def(v7, num, "MAX_VALUE", 9, attrs_desc, - v7_mk_number(v7, 1.7976931348623157e+308)); - v7_def(v7, num, "MIN_VALUE", 9, attrs_desc, v7_mk_number(v7, 5e-324)); -#if V7_ENABLE__NUMBER__NEGATIVE_INFINITY - v7_def(v7, num, "NEGATIVE_INFINITY", 17, attrs_desc, - v7_mk_number(v7, -INFINITY)); -#endif -#if V7_ENABLE__NUMBER__POSITIVE_INFINITY - v7_def(v7, num, "POSITIVE_INFINITY", 17, attrs_desc, - v7_mk_number(v7, INFINITY)); -#endif - v7_def(v7, num, "NaN", 3, attrs_desc, V7_TAG_NAN); - - v7_def(v7, v7->vals.global_object, "NaN", 3, attrs_desc, V7_TAG_NAN); - v7_def(v7, v7->vals.global_object, "isNaN", 5, V7_DESC_ENUMERABLE(0), - v7_mk_cfunction(n_isNaN)); -} - -#if defined(__cplusplus) -} -#endif /* __cplusplus */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_json.c" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* Amalgamated: #include "v7/src/internal.h" */ -/* Amalgamated: #include "v7/src/stdlib.h" */ -/* Amalgamated: #include "v7/src/core.h" */ -/* Amalgamated: #include "v7/src/object.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "v7/src/string.h" */ -/* Amalgamated: #include "v7/src/primitive.h" */ - -#if defined(__cplusplus) -extern "C" { -#endif /* __cplusplus */ - -#if defined(V7_ALT_JSON_PARSE) -extern enum v7_err v7_alt_json_parse(struct v7 *v7, v7_val_t json_string, - v7_val_t *res); -#endif - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Json_stringify(struct v7 *v7, v7_val_t *res) { - val_t arg0 = v7_arg(v7, 0); - char buf[100], *p = v7_to_json(v7, arg0, buf, sizeof(buf)); - *res = v7_mk_string(v7, p, strlen(p), 1); - - if (p != buf) free(p); - return V7_OK; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Json_parse(struct v7 *v7, v7_val_t *res) { - v7_val_t arg = v7_arg(v7, 0); - enum v7_err rcode = V7_OK; -#if defined(V7_ALT_JSON_PARSE) - rcode = v7_alt_json_parse(v7, arg, res); -#else - rcode = std_eval(v7, arg, V7_UNDEFINED, 1, res); -#endif - return rcode; -} - -V7_PRIVATE void init_json(struct v7 *v7) { - val_t o = v7_mk_object(v7); - set_method(v7, o, "stringify", Json_stringify, 1); - set_method(v7, o, "parse", Json_parse, 1); - v7_def(v7, v7->vals.global_object, "JSON", 4, V7_DESC_ENUMERABLE(0), o); -} - -#if defined(__cplusplus) -} -#endif /* __cplusplus */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_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/gc.h" */ -/* Amalgamated: #include "v7/src/eval.h" */ -/* Amalgamated: #include "v7/src/std_string.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "v7/src/core.h" */ -/* Amalgamated: #include "v7/src/function.h" */ -/* Amalgamated: #include "v7/src/array.h" */ -/* Amalgamated: #include "v7/src/object.h" */ -/* Amalgamated: #include "v7/src/exceptions.h" */ - -#if defined(__cplusplus) -extern "C" { -#endif /* __cplusplus */ - -struct a_sort_data { - val_t sort_func; -}; - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_ctor(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - unsigned long i, len; - - (void) v7; - *res = v7_mk_array(v7); - /* - * The interpreter passes dense array to C functions. - * However dense array implementation is not yet complete - * so we don't want to propagate them at each call to Array() - */ - len = v7_argc(v7); - for (i = 0; i < len; i++) { - rcode = v7_array_set_throwing(v7, *res, i, v7_arg(v7, i), NULL); - if (rcode != V7_OK) { - goto clean; - } - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_push(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - int i, len = v7_argc(v7); - - *res = V7_UNDEFINED; - - for (i = 0; i < len; i++) { - *res = v7_arg(v7, i); - rcode = v7_array_push_throwing(v7, v7_get_this(v7), *res, NULL); - if (rcode != V7_OK) { - goto clean; - } - } - -/* - * TODO(dfrank) : we need to implement `length` as a real property, and here - * we need to set new length and return it (even if the object is not an - * array) - */ - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_get_length(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - long len = 0; - - if (is_prototype_of(v7, this_obj, v7->vals.array_prototype)) { - len = v7_array_length(v7, this_obj); - } - *res = v7_mk_number(v7, len); - - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_set_length(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t arg0 = v7_arg(v7, 0); - val_t this_obj = v7_get_this(v7); - long new_len = 0; - - rcode = to_long(v7, v7_arg(v7, 0), -1, &new_len); - if (rcode != V7_OK) { - goto clean; - } - - if (!v7_is_object(this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Array expected"); - goto clean; - } else if (new_len < 0 || - (v7_is_number(arg0) && (isnan(v7_get_double(v7, arg0)) || - isinf(v7_get_double(v7, arg0))))) { - rcode = v7_throwf(v7, RANGE_ERROR, "Invalid array length"); - goto clean; - } else { - struct v7_property **p, **next; - long index, max_index = -1; - - /* Remove all items with an index higher than new_len */ - for (p = &get_object_struct(this_obj)->properties; *p != NULL; p = next) { - size_t n; - const char *s = v7_get_string(v7, &p[0]->name, &n); - next = &p[0]->next; - index = strtol(s, NULL, 10); - if (index >= new_len) { - v7_destroy_property(p); - *p = *next; - next = p; - } else if (index > max_index) { - max_index = index; - } - } - - /* If we have to expand, insert an item with appropriate index */ - if (new_len > 0 && max_index < new_len - 1) { - char buf[40]; - c_snprintf(buf, sizeof(buf), "%ld", new_len - 1); - v7_set(v7, this_obj, buf, strlen(buf), V7_UNDEFINED); - } - } - - *res = v7_mk_number(v7, new_len); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -static enum v7_err a_cmp(struct v7 *v7, void *user_data, const void *pa, - const void *pb, int *res) { - enum v7_err rcode = V7_OK; - struct a_sort_data *sort_data = (struct a_sort_data *) user_data; - val_t a = *(val_t *) pa, b = *(val_t *) pb, func = sort_data->sort_func; - - if (v7_is_callable(v7, func)) { - int saved_inhibit_gc = v7->inhibit_gc; - val_t vres = V7_UNDEFINED, args = v7_mk_dense_array(v7); - v7_array_push(v7, args, a); - v7_array_push(v7, args, b); - v7->inhibit_gc = 0; - rcode = b_apply(v7, func, V7_UNDEFINED, args, 0, &vres); - if (rcode != V7_OK) { - goto clean; - } - v7->inhibit_gc = saved_inhibit_gc; - *res = (int) -v7_get_double(v7, vres); - goto clean; - } else { - char sa[100], sb[100]; - - rcode = to_string(v7, a, NULL, sa, sizeof(sa), NULL); - if (rcode != V7_OK) { - goto clean; - } - - rcode = to_string(v7, b, NULL, sb, sizeof(sb), NULL); - if (rcode != V7_OK) { - goto clean; - } - - sa[sizeof(sa) - 1] = sb[sizeof(sb) - 1] = '\0'; - *res = strcmp(sb, sa); - goto clean; - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -static enum v7_err a_partition(struct v7 *v7, val_t *a, int l, int r, - void *user_data, int *res) { - enum v7_err rcode = V7_OK; - val_t t, pivot = a[l]; - int i = l, j = r + 1; - - for (;;) { - while (1) { - ++i; - - if (i <= r) { - int tmp = 0; - rcode = a_cmp(v7, user_data, &a[i], &pivot, &tmp); - if (rcode != V7_OK) { - goto clean; - } - - if (tmp > 0) { - break; - } - } else { - break; - } - } - while (1) { - int tmp = 0; - --j; - - rcode = a_cmp(v7, user_data, &a[j], &pivot, &tmp); - if (rcode != V7_OK) { - goto clean; - } - - if (tmp <= 0) { - break; - } - } - if (i >= j) break; - t = a[i]; - a[i] = a[j]; - a[j] = t; - } - t = a[l]; - a[l] = a[j]; - a[j] = t; - - *res = j; -clean: - return rcode; -} - -WARN_UNUSED_RESULT -static enum v7_err a_qsort(struct v7 *v7, val_t *a, int l, int r, - void *user_data) { - enum v7_err rcode = V7_OK; - if (l < r) { - int j = 0; - rcode = a_partition(v7, a, l, r, user_data, &j); - if (rcode != V7_OK) { - goto clean; - } - - rcode = a_qsort(v7, a, l, j - 1, user_data); - if (rcode != V7_OK) { - goto clean; - } - - rcode = a_qsort(v7, a, j + 1, r, user_data); - if (rcode != V7_OK) { - goto clean; - } - } -clean: - return rcode; -} - -WARN_UNUSED_RESULT -static enum v7_err a_sort(struct v7 *v7, - enum v7_err (*sorting_func)(struct v7 *v7, void *, - const void *, - const void *, int *res), - v7_val_t *res) { - enum v7_err rcode = V7_OK; - int i = 0, len = 0; - val_t *arr = NULL; - val_t arg0 = v7_arg(v7, 0); - - *res = v7_get_this(v7); - len = v7_array_length(v7, *res); - - if (!v7_is_object(*res)) { - goto clean; - } - - arr = (val_t *) malloc(len * sizeof(arr[0])); - - assert(*res != v7->vals.global_object); - - for (i = 0; i < len; i++) { - arr[i] = v7_array_get(v7, *res, i); - } - - /* TODO(dfrank): sorting_func isn't actually used! something is wrong here */ - if (sorting_func != NULL) { - struct a_sort_data sort_data; - sort_data.sort_func = arg0; - rcode = a_qsort(v7, arr, 0, len - 1, &sort_data); - if (rcode != V7_OK) { - goto clean; - } - } - - for (i = 0; i < len; i++) { - v7_array_set(v7, *res, i, arr[len - (i + 1)]); - } - -clean: - if (arr != NULL) { - free(arr); - } - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_sort(struct v7 *v7, v7_val_t *res) { - return a_sort(v7, a_cmp, res); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_reverse(struct v7 *v7, v7_val_t *res) { - return a_sort(v7, NULL, res); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_join(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t arg0 = v7_arg(v7, 0); - size_t sep_size = 0; - const char *sep = NULL; - - *res = V7_UNDEFINED; - - /* Get pointer to the separator string */ - if (!v7_is_string(arg0)) { - /* If no separator is provided, use comma */ - arg0 = v7_mk_string(v7, ",", 1, 1); - } - sep = v7_get_string(v7, &arg0, &sep_size); - - /* Do the actual join */ - if (is_prototype_of(v7, this_obj, v7->vals.array_prototype)) { - struct mbuf m; - char buf[100], *p; - long i, n, num_elems = v7_array_length(v7, this_obj); - - mbuf_init(&m, 0); - - for (i = 0; i < num_elems; i++) { - /* Append separator */ - if (i > 0) { - mbuf_append(&m, sep, sep_size); - } - - /* Append next item from an array */ - p = buf; - { - size_t tmp; - rcode = to_string(v7, v7_array_get(v7, this_obj, i), NULL, buf, - sizeof(buf), &tmp); - if (rcode != V7_OK) { - goto clean; - } - n = tmp; - } - if (n > (long) sizeof(buf)) { - p = (char *) malloc(n + 1); - rcode = to_string(v7, v7_array_get(v7, this_obj, i), NULL, p, n, NULL); - if (rcode != V7_OK) { - goto clean; - } - } - mbuf_append(&m, p, n); - if (p != buf) { - free(p); - } - } - - /* mbuf contains concatenated string now. Copy it to the result. */ - *res = v7_mk_string(v7, m.buf, m.len, 1); - mbuf_free(&m); - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_toString(struct v7 *v7, v7_val_t *res) { - return Array_join(v7, res); -} - -WARN_UNUSED_RESULT -static enum v7_err a_splice(struct v7 *v7, int mutate, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - long i, len = v7_array_length(v7, this_obj); - long num_args = v7_argc(v7); - long elems_to_insert = num_args > 2 ? num_args - 2 : 0; - long arg0, arg1; - - if (!v7_is_object(this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, - "Array.splice or Array.slice called on non-object value"); - goto clean; - } - - *res = v7_mk_dense_array(v7); - - rcode = to_long(v7, v7_arg(v7, 0), 0, &arg0); - if (rcode != V7_OK) { - goto clean; - } - - rcode = to_long(v7, v7_arg(v7, 1), len, &arg1); - if (rcode != V7_OK) { - goto clean; - } - - /* Bounds check */ - if (!mutate && len <= 0) { - goto clean; - } - if (arg0 < 0) arg0 = len + arg0; - if (arg0 < 0) arg0 = 0; - if (arg0 > len) arg0 = len; - if (mutate) { - if (arg1 < 0) arg1 = 0; - arg1 += arg0; - } else if (arg1 < 0) { - arg1 = len + arg1; - } - - /* Create return value - slice */ - for (i = arg0; i < arg1 && i < len; i++) { - rcode = - v7_array_push_throwing(v7, *res, v7_array_get(v7, this_obj, i), NULL); - if (rcode != V7_OK) { - goto clean; - } - } - - if (mutate && get_object_struct(this_obj)->attributes & V7_OBJ_DENSE_ARRAY) { - /* - * dense arrays are spliced by memmoving leaving the trailing - * space allocated for future appends. - * TODO(mkm): figure out if trimming is better - */ - struct v7_property *p = - v7_get_own_property2(v7, this_obj, "", 0, _V7_PROPERTY_HIDDEN); - struct mbuf *abuf; - if (p == NULL) { - goto clean; - } - abuf = (struct mbuf *) v7_get_ptr(v7, p->value); - if (abuf == NULL) { - goto clean; - } - - memmove(abuf->buf + arg0 * sizeof(val_t), abuf->buf + arg1 * sizeof(val_t), - (len - arg1) * sizeof(val_t)); - abuf->len -= (arg1 - arg0) * sizeof(val_t); - } else if (mutate) { - /* If splicing, modify this_obj array: remove spliced sub-array */ - struct v7_property **p, **next; - long i; - - for (p = &get_object_struct(this_obj)->properties; *p != NULL; p = next) { - size_t n; - const char *s = v7_get_string(v7, &p[0]->name, &n); - next = &p[0]->next; - i = strtol(s, NULL, 10); - if (i >= arg0 && i < arg1) { - /* Remove items from spliced sub-array */ - v7_destroy_property(p); - *p = *next; - next = p; - } else if (i >= arg1) { - /* Modify indices of the elements past sub-array */ - char key[20]; - size_t n = c_snprintf(key, sizeof(key), "%ld", - i - (arg1 - arg0) + elems_to_insert); - p[0]->name = v7_mk_string(v7, key, n, 1); - } - } - - /* Insert optional extra elements */ - for (i = 2; i < num_args; i++) { - char key[20]; - size_t n = c_snprintf(key, sizeof(key), "%ld", arg0 + i - 2); - rcode = set_property(v7, this_obj, key, n, v7_arg(v7, i), NULL); - if (rcode != V7_OK) { - goto clean; - } - } - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_slice(struct v7 *v7, v7_val_t *res) { - return a_splice(v7, 0, res); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_splice(struct v7 *v7, v7_val_t *res) { - return a_splice(v7, 1, res); -} - -static void a_prep1(struct v7 *v7, val_t t, val_t *a0, val_t *a1) { - *a0 = v7_arg(v7, 0); - *a1 = v7_arg(v7, 1); - if (v7_is_undefined(*a1)) { - *a1 = t; - } -} - -/* - * Call callback function `cb`, passing `this_obj` as `this`, with the - * following arguments: - * - * cb(v, n, this_obj); - * - */ -WARN_UNUSED_RESULT -static enum v7_err a_prep2(struct v7 *v7, val_t cb, val_t v, val_t n, - val_t this_obj, val_t *res) { - enum v7_err rcode = V7_OK; - int saved_inhibit_gc = v7->inhibit_gc; - val_t args = v7_mk_dense_array(v7); - - *res = v7_mk_dense_array(v7); - - v7_own(v7, &args); - - v7_array_push(v7, args, v); - v7_array_push(v7, args, n); - v7_array_push(v7, args, this_obj); - - v7->inhibit_gc = 0; - rcode = b_apply(v7, cb, this_obj, args, 0, res); - if (rcode != V7_OK) { - goto clean; - } - -clean: - v7->inhibit_gc = saved_inhibit_gc; - v7_disown(v7, &args); - - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_forEach(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t v = V7_UNDEFINED, cb = v7_arg(v7, 0); - unsigned long len, i; - int has; - /* a_prep2 uninhibits GC when calling cb */ - struct gc_tmp_frame vf = new_tmp_frame(v7); - - if (!v7_is_object(this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Array expected"); - goto clean; - } - - if (!v7_is_callable(v7, cb)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Function expected"); - goto clean; - } - - tmp_stack_push(&vf, &v); - - len = v7_array_length(v7, this_obj); - for (i = 0; i < len; i++) { - v = v7_array_get2(v7, this_obj, i, &has); - if (!has) continue; - - rcode = a_prep2(v7, cb, v, v7_mk_number(v7, i), this_obj, res); - if (rcode != V7_OK) { - goto clean; - } - } - -clean: - tmp_frame_cleanup(&vf); - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_map(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t arg0, arg1, el, v; - unsigned long len, i; - int has; - /* a_prep2 uninhibits GC when calling cb */ - struct gc_tmp_frame vf = new_tmp_frame(v7); - - if (!v7_is_object(this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Array expected"); - goto clean; - } else { - a_prep1(v7, this_obj, &arg0, &arg1); - *res = v7_mk_dense_array(v7); - len = v7_array_length(v7, this_obj); - - tmp_stack_push(&vf, &arg0); - tmp_stack_push(&vf, &arg1); - tmp_stack_push(&vf, &v); - - for (i = 0; i < len; i++) { - v = v7_array_get2(v7, this_obj, i, &has); - if (!has) continue; - rcode = a_prep2(v7, arg0, v, v7_mk_number(v7, i), arg1, &el); - if (rcode != V7_OK) { - goto clean; - } - - rcode = v7_array_set_throwing(v7, *res, i, el, NULL); - if (rcode != V7_OK) { - goto clean; - } - } - } - -clean: - tmp_frame_cleanup(&vf); - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_every(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t arg0, arg1, el, v; - unsigned long i, len; - int has; - /* a_prep2 uninhibits GC when calling cb */ - struct gc_tmp_frame vf = new_tmp_frame(v7); - - *res = v7_mk_boolean(v7, 0); - - if (!v7_is_object(this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Array expected"); - goto clean; - } else { - a_prep1(v7, this_obj, &arg0, &arg1); - - tmp_stack_push(&vf, &arg0); - tmp_stack_push(&vf, &arg1); - tmp_stack_push(&vf, &v); - - len = v7_array_length(v7, this_obj); - for (i = 0; i < len; i++) { - v = v7_array_get2(v7, this_obj, i, &has); - if (!has) continue; - rcode = a_prep2(v7, arg0, v, v7_mk_number(v7, i), arg1, &el); - if (rcode != V7_OK) { - goto clean; - } - if (!v7_is_truthy(v7, el)) { - *res = v7_mk_boolean(v7, 0); - goto clean; - } - } - } - - *res = v7_mk_boolean(v7, 1); - -clean: - tmp_frame_cleanup(&vf); - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_some(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t arg0, arg1, el, v; - unsigned long i, len; - int has; - /* a_prep2 uninhibits GC when calling cb */ - struct gc_tmp_frame vf = new_tmp_frame(v7); - - *res = v7_mk_boolean(v7, 1); - - if (!v7_is_object(this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Array expected"); - goto clean; - } else { - a_prep1(v7, this_obj, &arg0, &arg1); - - tmp_stack_push(&vf, &arg0); - tmp_stack_push(&vf, &arg1); - tmp_stack_push(&vf, &v); - - len = v7_array_length(v7, this_obj); - for (i = 0; i < len; i++) { - v = v7_array_get2(v7, this_obj, i, &has); - if (!has) continue; - rcode = a_prep2(v7, arg0, v, v7_mk_number(v7, i), arg1, &el); - if (rcode != V7_OK) { - goto clean; - } - if (v7_is_truthy(v7, el)) { - *res = v7_mk_boolean(v7, 1); - goto clean; - } - } - } - - *res = v7_mk_boolean(v7, 0); - -clean: - tmp_frame_cleanup(&vf); - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_filter(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t arg0, arg1, el, v; - unsigned long len, i; - int has; - /* a_prep2 uninhibits GC when calling cb */ - struct gc_tmp_frame vf = new_tmp_frame(v7); - - if (!v7_is_object(this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Array expected"); - goto clean; - } else { - a_prep1(v7, this_obj, &arg0, &arg1); - *res = v7_mk_dense_array(v7); - len = v7_array_length(v7, this_obj); - - tmp_stack_push(&vf, &arg0); - tmp_stack_push(&vf, &arg1); - tmp_stack_push(&vf, &v); - - for (i = 0; i < len; i++) { - v = v7_array_get2(v7, this_obj, i, &has); - if (!has) continue; - rcode = a_prep2(v7, arg0, v, v7_mk_number(v7, i), arg1, &el); - if (rcode != V7_OK) { - goto clean; - } - if (v7_is_truthy(v7, el)) { - rcode = v7_array_push_throwing(v7, *res, v, NULL); - if (rcode != V7_OK) { - goto clean; - } - } - } - } - -clean: - tmp_frame_cleanup(&vf); - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_concat(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - size_t i, j, len; - val_t saved_args; - - if (!v7_is_array(v7, this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Array expected"); - goto clean; - } - - len = v7_argc(v7); - - /* - * reuse a_splice but override it's arguments. a_splice - * internally uses a lot of helpers that fetch arguments - * from the v7 context. - * TODO(mkm): we need a better helper call another cfunction - * from a cfunction. - */ - saved_args = v7->vals.arguments; - v7->vals.arguments = V7_UNDEFINED; - rcode = a_splice(v7, 1, res); - if (rcode != V7_OK) { - goto clean; - } - v7->vals.arguments = saved_args; - - for (i = 0; i < len; i++) { - val_t a = v7_arg(v7, i); - if (!v7_is_array(v7, a)) { - rcode = v7_array_push_throwing(v7, *res, a, NULL); - if (rcode != V7_OK) { - goto clean; - } - } else { - size_t alen = v7_array_length(v7, a); - for (j = 0; j < alen; j++) { - rcode = v7_array_push_throwing(v7, *res, v7_array_get(v7, a, j), NULL); - if (rcode != V7_OK) { - goto clean; - } - } - } - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Array_isArray(struct v7 *v7, v7_val_t *res) { - val_t arg0 = v7_arg(v7, 0); - *res = v7_mk_boolean(v7, v7_is_array(v7, arg0)); - return V7_OK; -} - -V7_PRIVATE void init_array(struct v7 *v7) { - val_t ctor = mk_cfunction_obj(v7, Array_ctor, 1); - val_t length = v7_mk_dense_array(v7); - - v7_set(v7, ctor, "prototype", 9, v7->vals.array_prototype); - set_method(v7, ctor, "isArray", Array_isArray, 1); - v7_set(v7, v7->vals.global_object, "Array", 5, ctor); - v7_def(v7, v7->vals.array_prototype, "constructor", ~0, _V7_DESC_HIDDEN(1), - ctor); - v7_set(v7, ctor, "name", 4, v7_mk_string(v7, "Array", ~0, 1)); - - set_method(v7, v7->vals.array_prototype, "concat", Array_concat, 1); - set_method(v7, v7->vals.array_prototype, "every", Array_every, 1); - set_method(v7, v7->vals.array_prototype, "filter", Array_filter, 1); - set_method(v7, v7->vals.array_prototype, "forEach", Array_forEach, 1); - set_method(v7, v7->vals.array_prototype, "join", Array_join, 1); - set_method(v7, v7->vals.array_prototype, "map", Array_map, 1); - set_method(v7, v7->vals.array_prototype, "push", Array_push, 1); - set_method(v7, v7->vals.array_prototype, "reverse", Array_reverse, 0); - set_method(v7, v7->vals.array_prototype, "slice", Array_slice, 2); - set_method(v7, v7->vals.array_prototype, "some", Array_some, 1); - set_method(v7, v7->vals.array_prototype, "sort", Array_sort, 1); - set_method(v7, v7->vals.array_prototype, "splice", Array_splice, 2); - set_method(v7, v7->vals.array_prototype, "toString", Array_toString, 0); - - v7_array_set(v7, length, 0, v7_mk_cfunction(Array_get_length)); - v7_array_set(v7, length, 1, v7_mk_cfunction(Array_set_length)); - v7_def(v7, v7->vals.array_prototype, "length", 6, - V7_DESC_ENUMERABLE(0) | V7_DESC_GETTER(1) | V7_DESC_SETTER(1), length); -} - -#if defined(__cplusplus) -} -#endif /* __cplusplus */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_boolean.c" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* Amalgamated: #include "v7/src/internal.h" */ -/* Amalgamated: #include "v7/src/std_object.h" */ -/* Amalgamated: #include "v7/src/core.h" */ -/* Amalgamated: #include "v7/src/function.h" */ -/* Amalgamated: #include "v7/src/object.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "v7/src/primitive.h" */ -/* Amalgamated: #include "v7/src/exceptions.h" */ -/* Amalgamated: #include "v7/src/string.h" */ - -#if defined(__cplusplus) -extern "C" { -#endif /* __cplusplus */ - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Boolean_ctor(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - - *res = to_boolean_v(v7, v7_arg(v7, 0)); - - if (v7_is_generic_object(this_obj) && this_obj != v7->vals.global_object) { - /* called as "new Boolean(...)" */ - obj_prototype_set(v7, get_object_struct(this_obj), - get_object_struct(v7->vals.boolean_prototype)); - v7_def(v7, this_obj, "", 0, _V7_DESC_HIDDEN(1), *res); - - /* - * implicitly returning `this`: `call_cfunction()` in bcode.c will do - * that for us - */ - } - - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Boolean_valueOf(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - if (!v7_is_boolean(this_obj) && - (v7_is_object(this_obj) && - v7_get_proto(v7, this_obj) != v7->vals.boolean_prototype)) { - rcode = v7_throwf(v7, TYPE_ERROR, - "Boolean.valueOf called on non-boolean object"); - goto clean; - } - - rcode = Obj_valueOf(v7, res); - if (rcode != V7_OK) { - goto clean; - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Boolean_toString(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - - *res = V7_UNDEFINED; - - if (this_obj == v7->vals.boolean_prototype) { - *res = v7_mk_string(v7, "false", 5, 1); - goto clean; - } - - if (!v7_is_boolean(this_obj) && - !(v7_is_generic_object(this_obj) && - is_prototype_of(v7, this_obj, v7->vals.boolean_prototype))) { - rcode = v7_throwf(v7, TYPE_ERROR, - "Boolean.toString called on non-boolean object"); - goto clean; - } - - rcode = obj_value_of(v7, this_obj, res); - if (rcode != V7_OK) { - goto clean; - } - - rcode = primitive_to_str(v7, *res, res, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - -clean: - return rcode; -} - -V7_PRIVATE void init_boolean(struct v7 *v7) { - val_t ctor = mk_cfunction_obj_with_proto(v7, Boolean_ctor, 1, - v7->vals.boolean_prototype); - v7_set(v7, v7->vals.global_object, "Boolean", 7, ctor); - - set_cfunc_prop(v7, v7->vals.boolean_prototype, "valueOf", Boolean_valueOf); - set_cfunc_prop(v7, v7->vals.boolean_prototype, "toString", Boolean_toString); -} - -#if defined(__cplusplus) -} -#endif /* __cplusplus */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_math.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/primitive.h" */ - -#if V7_ENABLE__Math - -#if defined(__cplusplus) -extern "C" { -#endif /* __cplusplus */ - -#ifdef __WATCOM__ -int matherr(struct _exception *exc) { - if (exc->type == DOMAIN) { - exc->retval = NAN; - return 0; - } -} -#endif - -#if V7_ENABLE__Math__abs || V7_ENABLE__Math__acos || V7_ENABLE__Math__asin || \ - V7_ENABLE__Math__atan || V7_ENABLE__Math__ceil || V7_ENABLE__Math__cos || \ - V7_ENABLE__Math__exp || V7_ENABLE__Math__floor || V7_ENABLE__Math__log || \ - V7_ENABLE__Math__round || V7_ENABLE__Math__sin || V7_ENABLE__Math__sqrt || \ - V7_ENABLE__Math__tan -WARN_UNUSED_RESULT -static enum v7_err m_one_arg(struct v7 *v7, double (*f)(double), val_t *res) { - enum v7_err rcode = V7_OK; - val_t arg0 = v7_arg(v7, 0); - double d0 = v7_get_double(v7, arg0); -#ifdef V7_BROKEN_NAN - if (isnan(d0)) { - *res = V7_TAG_NAN; - goto clean; - } -#endif - *res = v7_mk_number(v7, f(d0)); - goto clean; - -clean: - return rcode; -} -#endif /* V7_ENABLE__Math__* */ - -#if V7_ENABLE__Math__pow || V7_ENABLE__Math__atan2 -WARN_UNUSED_RESULT -static enum v7_err m_two_arg(struct v7 *v7, double (*f)(double, double), - val_t *res) { - enum v7_err rcode = V7_OK; - val_t arg0 = v7_arg(v7, 0); - val_t arg1 = v7_arg(v7, 1); - double d0 = v7_get_double(v7, arg0); - double d1 = v7_get_double(v7, arg1); -#ifdef V7_BROKEN_NAN - /* pow(NaN,0) == 1, doesn't fix atan2, but who cares */ - if (isnan(d1)) { - *res = V7_TAG_NAN; - goto clean; - } -#endif - *res = v7_mk_number(v7, f(d0, d1)); - goto clean; - -clean: - return rcode; -} -#endif /* V7_ENABLE__Math__pow || V7_ENABLE__Math__atan2 */ - -#define DEFINE_WRAPPER(name, func) \ - WARN_UNUSED_RESULT \ - V7_PRIVATE enum v7_err Math_##name(struct v7 *v7, v7_val_t *res) { \ - return func(v7, name, res); \ - } - -/* Visual studio 2012+ has round() */ -#if V7_ENABLE__Math__round && \ - ((defined(V7_WINDOWS) && _MSC_VER < 1700) || defined(__WATCOM__)) -static double round(double n) { - return n; -} -#endif - -#if V7_ENABLE__Math__abs -DEFINE_WRAPPER(fabs, m_one_arg) -#endif -#if V7_ENABLE__Math__acos -DEFINE_WRAPPER(acos, m_one_arg) -#endif -#if V7_ENABLE__Math__asin -DEFINE_WRAPPER(asin, m_one_arg) -#endif -#if V7_ENABLE__Math__atan -DEFINE_WRAPPER(atan, m_one_arg) -#endif -#if V7_ENABLE__Math__atan2 -DEFINE_WRAPPER(atan2, m_two_arg) -#endif -#if V7_ENABLE__Math__ceil -DEFINE_WRAPPER(ceil, m_one_arg) -#endif -#if V7_ENABLE__Math__cos -DEFINE_WRAPPER(cos, m_one_arg) -#endif -#if V7_ENABLE__Math__exp -DEFINE_WRAPPER(exp, m_one_arg) -#endif -#if V7_ENABLE__Math__floor -DEFINE_WRAPPER(floor, m_one_arg) -#endif -#if V7_ENABLE__Math__log -DEFINE_WRAPPER(log, m_one_arg) -#endif -#if V7_ENABLE__Math__pow -DEFINE_WRAPPER(pow, m_two_arg) -#endif -#if V7_ENABLE__Math__round -DEFINE_WRAPPER(round, m_one_arg) -#endif -#if V7_ENABLE__Math__sin -DEFINE_WRAPPER(sin, m_one_arg) -#endif -#if V7_ENABLE__Math__sqrt -DEFINE_WRAPPER(sqrt, m_one_arg) -#endif -#if V7_ENABLE__Math__tan -DEFINE_WRAPPER(tan, m_one_arg) -#endif - -#if V7_ENABLE__Math__random -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Math_random(struct v7 *v7, v7_val_t *res) { - (void) v7; - *res = v7_mk_number(v7, (double) rand() / RAND_MAX); - return V7_OK; -} -#endif /* V7_ENABLE__Math__random */ - -#if V7_ENABLE__Math__min || V7_ENABLE__Math__max -WARN_UNUSED_RESULT -static enum v7_err min_max(struct v7 *v7, int is_min, val_t *res) { - enum v7_err rcode = V7_OK; - double dres = NAN; - int i, len = v7_argc(v7); - - for (i = 0; i < len; i++) { - double v = v7_get_double(v7, v7_arg(v7, i)); - if (isnan(dres) || (is_min && v < dres) || (!is_min && v > dres)) { - dres = v; - } - } - - *res = v7_mk_number(v7, dres); - - return rcode; -} -#endif /* V7_ENABLE__Math__min || V7_ENABLE__Math__max */ - -#if V7_ENABLE__Math__min -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Math_min(struct v7 *v7, v7_val_t *res) { - return min_max(v7, 1, res); -} -#endif - -#if V7_ENABLE__Math__max -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Math_max(struct v7 *v7, v7_val_t *res) { - return min_max(v7, 0, res); -} -#endif - -V7_PRIVATE void init_math(struct v7 *v7) { - val_t math = v7_mk_object(v7); - -#if V7_ENABLE__Math__abs - set_cfunc_prop(v7, math, "abs", Math_fabs); -#endif -#if V7_ENABLE__Math__acos - set_cfunc_prop(v7, math, "acos", Math_acos); -#endif -#if V7_ENABLE__Math__asin - set_cfunc_prop(v7, math, "asin", Math_asin); -#endif -#if V7_ENABLE__Math__atan - set_cfunc_prop(v7, math, "atan", Math_atan); -#endif -#if V7_ENABLE__Math__atan2 - set_cfunc_prop(v7, math, "atan2", Math_atan2); -#endif -#if V7_ENABLE__Math__ceil - set_cfunc_prop(v7, math, "ceil", Math_ceil); -#endif -#if V7_ENABLE__Math__cos - set_cfunc_prop(v7, math, "cos", Math_cos); -#endif -#if V7_ENABLE__Math__exp - set_cfunc_prop(v7, math, "exp", Math_exp); -#endif -#if V7_ENABLE__Math__floor - set_cfunc_prop(v7, math, "floor", Math_floor); -#endif -#if V7_ENABLE__Math__log - set_cfunc_prop(v7, math, "log", Math_log); -#endif -#if V7_ENABLE__Math__max - set_cfunc_prop(v7, math, "max", Math_max); -#endif -#if V7_ENABLE__Math__min - set_cfunc_prop(v7, math, "min", Math_min); -#endif -#if V7_ENABLE__Math__pow - set_cfunc_prop(v7, math, "pow", Math_pow); -#endif -#if V7_ENABLE__Math__random - /* Incorporate our pointer into the RNG. - * If srand() has not been called before, this will provide some randomness. - * If it has, it will hopefully not make things worse. - */ - srand(rand() ^ ((uintptr_t) v7)); - set_cfunc_prop(v7, math, "random", Math_random); -#endif -#if V7_ENABLE__Math__round - set_cfunc_prop(v7, math, "round", Math_round); -#endif -#if V7_ENABLE__Math__sin - set_cfunc_prop(v7, math, "sin", Math_sin); -#endif -#if V7_ENABLE__Math__sqrt - set_cfunc_prop(v7, math, "sqrt", Math_sqrt); -#endif -#if V7_ENABLE__Math__tan - set_cfunc_prop(v7, math, "tan", Math_tan); -#endif - -#if V7_ENABLE__Math__constants - v7_set(v7, math, "E", 1, v7_mk_number(v7, M_E)); - v7_set(v7, math, "PI", 2, v7_mk_number(v7, M_PI)); - v7_set(v7, math, "LN2", 3, v7_mk_number(v7, M_LN2)); - v7_set(v7, math, "LN10", 4, v7_mk_number(v7, M_LN10)); - v7_set(v7, math, "LOG2E", 5, v7_mk_number(v7, M_LOG2E)); - v7_set(v7, math, "LOG10E", 6, v7_mk_number(v7, M_LOG10E)); - v7_set(v7, math, "SQRT1_2", 7, v7_mk_number(v7, M_SQRT1_2)); - v7_set(v7, math, "SQRT2", 5, v7_mk_number(v7, M_SQRT2)); -#endif - - v7_set(v7, v7->vals.global_object, "Math", 4, math); -} - -#if defined(__cplusplus) -} -#endif /* __cplusplus */ - -#endif /* V7_ENABLE__Math */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_string.c" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* Amalgamated: #include "common/utf.h" */ -/* Amalgamated: #include "v7/src/internal.h" */ -/* Amalgamated: #include "v7/src/std_string.h" */ -/* Amalgamated: #include "v7/src/core.h" */ -/* Amalgamated: #include "v7/src/function.h" */ -/* Amalgamated: #include "v7/src/string.h" */ -/* Amalgamated: #include "v7/src/slre.h" */ -/* Amalgamated: #include "v7/src/std_regex.h" */ -/* Amalgamated: #include "v7/src/std_object.h" */ -/* Amalgamated: #include "v7/src/eval.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "v7/src/array.h" */ -/* Amalgamated: #include "v7/src/object.h" */ -/* Amalgamated: #include "v7/src/regexp.h" */ -/* Amalgamated: #include "v7/src/exceptions.h" */ - -/* Substring implementations: RegExp-based and String-based {{{ */ - -/* - * Substring context: currently, used in Str_split() only, but will probably - * be used in Str_replace() and other functions as well. - * - * Needed to provide different implementation for RegExp or String arguments, - * keeping common parts reusable. - */ -struct _str_split_ctx { - /* implementation-specific data */ - union { -#if V7_ENABLE__RegExp - struct { - struct slre_prog *prog; - struct slre_loot loot; - } regexp; -#endif - - struct { - val_t sep; - } string; - } impl; - - struct v7 *v7; - - /* start and end of previous match (set by `p_exec()`) */ - const char *match_start; - const char *match_end; - - /* pointers to implementation functions */ - - /* - * Initialize context - */ - void (*p_init)(struct _str_split_ctx *ctx, struct v7 *v7, val_t sep); - - /* - * Look for the next match, set `match_start` and `match_end` to appropriate - * values. - * - * Returns 0 if match found, 1 otherwise (in accordance with `slre_exec()`) - */ - int (*p_exec)(struct _str_split_ctx *ctx, const char *start, const char *end); - -#if V7_ENABLE__RegExp - /* - * Add captured data to resulting array (for RegExp-based implementation only) - * - * Returns updated `elem` value - */ - long (*p_add_caps)(struct _str_split_ctx *ctx, val_t res, long elem, - long limit); -#endif -}; - -#if V7_ENABLE__RegExp -/* RegExp-based implementation of `p_init` in `struct _str_split_ctx` */ -static void subs_regexp_init(struct _str_split_ctx *ctx, struct v7 *v7, - val_t sep) { - ctx->v7 = v7; - ctx->impl.regexp.prog = v7_get_regexp_struct(v7, sep)->compiled_regexp; -} - -/* RegExp-based implementation of `p_exec` in `struct _str_split_ctx` */ -static int subs_regexp_exec(struct _str_split_ctx *ctx, const char *start, - const char *end) { - int ret = - slre_exec(ctx->impl.regexp.prog, 0, start, end, &ctx->impl.regexp.loot); - - ctx->match_start = ctx->impl.regexp.loot.caps[0].start; - ctx->match_end = ctx->impl.regexp.loot.caps[0].end; - - return ret; -} - -/* RegExp-based implementation of `p_add_caps` in `struct _str_split_ctx` */ -static long subs_regexp_split_add_caps(struct _str_split_ctx *ctx, val_t res, - long elem, long limit) { - int i; - for (i = 1; i < ctx->impl.regexp.loot.num_captures && elem < limit; i++) { - size_t cap_len = - ctx->impl.regexp.loot.caps[i].end - ctx->impl.regexp.loot.caps[i].start; - v7_array_push( - ctx->v7, res, - (ctx->impl.regexp.loot.caps[i].start != NULL) - ? v7_mk_string(ctx->v7, ctx->impl.regexp.loot.caps[i].start, - cap_len, 1) - : V7_UNDEFINED); - elem++; - } - return elem; -} -#endif - -/* String-based implementation of `p_init` in `struct _str_split_ctx` */ -static void subs_string_init(struct _str_split_ctx *ctx, struct v7 *v7, - val_t sep) { - ctx->v7 = v7; - ctx->impl.string.sep = sep; -} - -/* String-based implementation of `p_exec` in `struct _str_split_ctx` */ -static int subs_string_exec(struct _str_split_ctx *ctx, const char *start, - const char *end) { - int ret = 1; - size_t sep_len; - const char *psep = v7_get_string(ctx->v7, &ctx->impl.string.sep, &sep_len); - - if (sep_len == 0) { - /* separator is an empty string: match empty string */ - ctx->match_start = start; - ctx->match_end = start; - ret = 0; - } else { - size_t i; - for (i = 0; start <= (end - sep_len); ++i, start = utfnshift(start, 1)) { - if (memcmp(start, psep, sep_len) == 0) { - ret = 0; - ctx->match_start = start; - ctx->match_end = start + sep_len; - break; - } - } - } - - return ret; -} - -#if V7_ENABLE__RegExp -/* String-based implementation of `p_add_caps` in `struct _str_split_ctx` */ -static long subs_string_split_add_caps(struct _str_split_ctx *ctx, val_t res, - long elem, long limit) { - /* this is a stub function */ - (void) ctx; - (void) res; - (void) limit; - return elem; -} -#endif - -/* }}} */ - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err String_ctor(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t arg0 = v7_arg(v7, 0); - - *res = arg0; - - if (v7_argc(v7) == 0) { - *res = v7_mk_string(v7, NULL, 0, 1); - } else if (!v7_is_string(arg0)) { - rcode = to_string(v7, arg0, res, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - } - - if (v7_is_generic_object(this_obj) && this_obj != v7->vals.global_object) { - obj_prototype_set(v7, get_object_struct(this_obj), - get_object_struct(v7->vals.string_prototype)); - v7_def(v7, this_obj, "", 0, _V7_DESC_HIDDEN(1), *res); - /* - * implicitly returning `this`: `call_cfunction()` in bcode.c will do - * that for us - */ - goto clean; - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_fromCharCode(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - int i, num_args = v7_argc(v7); - - *res = v7_mk_string(v7, "", 0, 1); /* Empty string */ - - for (i = 0; i < num_args; i++) { - char buf[10]; - val_t arg = v7_arg(v7, i); - double d = v7_get_double(v7, arg); - Rune r = (Rune)((int32_t)(isnan(d) || isinf(d) ? 0 : d) & 0xffff); - int n = runetochar(buf, &r); - val_t s = v7_mk_string(v7, buf, n, 1); - *res = s_concat(v7, *res, s); - } - - return rcode; -} - -WARN_UNUSED_RESULT -static enum v7_err s_charCodeAt(struct v7 *v7, double *res) { - return v7_char_code_at(v7, v7_get_this(v7), v7_arg(v7, 0), res); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_charCodeAt(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - double dnum = 0; - - rcode = s_charCodeAt(v7, &dnum); - if (rcode != V7_OK) { - goto clean; - } - - *res = v7_mk_number(v7, dnum); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_charAt(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - double code = 0; - char buf[10] = {0}; - int len = 0; - - rcode = s_charCodeAt(v7, &code); - if (rcode != V7_OK) { - goto clean; - } - - if (!isnan(code)) { - Rune r = (Rune) code; - len = runetochar(buf, &r); - } - *res = v7_mk_string(v7, buf, len, 1); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_concat(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - int i, num_args = v7_argc(v7); - - rcode = to_string(v7, this_obj, res, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - for (i = 0; i < num_args; i++) { - val_t str = V7_UNDEFINED; - - rcode = to_string(v7, v7_arg(v7, i), &str, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - *res = s_concat(v7, *res, str); - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -static enum v7_err s_index_of(struct v7 *v7, int last, val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t arg0 = v7_arg(v7, 0); - size_t fromIndex = 0; - double dres = -1; - - if (!v7_is_undefined(arg0)) { - const char *p1, *p2, *end; - size_t i, len1, len2, bytecnt1, bytecnt2; - val_t sub = V7_UNDEFINED; - - rcode = to_string(v7, arg0, &sub, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - rcode = to_string(v7, this_obj, &this_obj, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - p1 = v7_get_string(v7, &this_obj, &bytecnt1); - p2 = v7_get_string(v7, &sub, &bytecnt2); - - if (bytecnt2 <= bytecnt1) { - end = p1 + bytecnt1; - len1 = utfnlen(p1, bytecnt1); - len2 = utfnlen(p2, bytecnt2); - - if (v7_argc(v7) > 1) { - /* `fromIndex` was provided. Normalize it */ - double d = 0; - { - val_t arg = v7_arg(v7, 1); - rcode = to_number_v(v7, arg, &arg); - if (rcode != V7_OK) { - goto clean; - } - d = v7_get_double(v7, arg); - } - if (isnan(d) || d < 0) { - d = 0.0; - } else if (isinf(d) || d > len1) { - d = len1; - } - fromIndex = d; - - /* adjust pointers accordingly to `fromIndex` */ - if (last) { - const char *end_tmp = utfnshift(p1, fromIndex + len2); - end = (end_tmp < end) ? end_tmp : end; - } else { - p1 = utfnshift(p1, fromIndex); - } - } - - /* - * TODO(dfrank): when `last` is set, start from the end and look - * backward. We'll need to improve `utfnshift()` for that, so that it can - * handle negative offsets. - */ - for (i = 0; p1 <= (end - bytecnt2); i++, p1 = utfnshift(p1, 1)) { - if (memcmp(p1, p2, bytecnt2) == 0) { - dres = i; - if (!last) break; - } - } - } - } - if (!last && dres >= 0) dres += fromIndex; - *res = v7_mk_number(v7, dres); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_valueOf(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - - if (!v7_is_string(this_obj) && - (v7_is_object(this_obj) && - v7_get_proto(v7, this_obj) != v7->vals.string_prototype)) { - rcode = - v7_throwf(v7, TYPE_ERROR, "String.valueOf called on non-string object"); - goto clean; - } - - rcode = Obj_valueOf(v7, res); - goto clean; - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_indexOf(struct v7 *v7, v7_val_t *res) { - return s_index_of(v7, 0, res); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_lastIndexOf(struct v7 *v7, v7_val_t *res) { - return s_index_of(v7, 1, res); -} - -#if V7_ENABLE__String__localeCompare -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_localeCompare(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t arg0 = V7_UNDEFINED; - val_t s = V7_UNDEFINED; - - rcode = to_string(v7, v7_arg(v7, 0), &arg0, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - rcode = to_string(v7, this_obj, &s, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - *res = v7_mk_number(v7, s_cmp(v7, s, arg0)); - -clean: - return rcode; -} -#endif - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_toString(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - - if (this_obj == v7->vals.string_prototype) { - *res = v7_mk_string(v7, "false", 5, 1); - goto clean; - } - - if (!v7_is_string(this_obj) && - !(v7_is_generic_object(this_obj) && - is_prototype_of(v7, this_obj, v7->vals.string_prototype))) { - rcode = v7_throwf(v7, TYPE_ERROR, - "String.toString called on non-string object"); - goto clean; - } - - rcode = obj_value_of(v7, this_obj, &this_obj); - if (rcode != V7_OK) { - goto clean; - } - - rcode = to_string(v7, this_obj, res, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - -clean: - return rcode; -} - -#if V7_ENABLE__RegExp -WARN_UNUSED_RESULT -enum v7_err call_regex_ctor(struct v7 *v7, val_t arg, val_t *res) { - /* TODO(mkm): make general helper out of this */ - enum v7_err rcode = V7_OK; - val_t saved_args = v7->vals.arguments, args = v7_mk_dense_array(v7); - v7_array_push(v7, args, arg); - v7->vals.arguments = args; - - rcode = Regex_ctor(v7, res); - if (rcode != V7_OK) { - goto clean; - } - v7->vals.arguments = saved_args; - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_match(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t so = V7_UNDEFINED, ro = V7_UNDEFINED; - long previousLastIndex = 0; - int lastMatch = 1, n = 0, flag_g; - struct v7_regexp *rxp; - - *res = V7_NULL; - - rcode = to_string(v7, this_obj, &so, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_argc(v7) == 0) { - rcode = v7_mk_regexp(v7, "", 0, "", 0, &ro); - if (rcode != V7_OK) { - goto clean; - } - } else { - rcode = obj_value_of(v7, v7_arg(v7, 0), &ro); - if (rcode != V7_OK) { - goto clean; - } - } - - if (!v7_is_regexp(v7, ro)) { - rcode = call_regex_ctor(v7, ro, &ro); - if (rcode != V7_OK) { - goto clean; - } - } - - rxp = v7_get_regexp_struct(v7, ro); - flag_g = slre_get_flags(rxp->compiled_regexp) & SLRE_FLAG_G; - if (!flag_g) { - rcode = rx_exec(v7, ro, so, 0, res); - goto clean; - } - - rxp->lastIndex = 0; - *res = v7_mk_dense_array(v7); - while (lastMatch) { - val_t result; - - rcode = rx_exec(v7, ro, so, 1, &result); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_is_null(result)) { - lastMatch = 0; - } else { - long thisIndex = rxp->lastIndex; - if (thisIndex == previousLastIndex) { - previousLastIndex = thisIndex + 1; - rxp->lastIndex = previousLastIndex; - } else { - previousLastIndex = thisIndex; - } - rcode = - v7_array_push_throwing(v7, *res, v7_array_get(v7, result, 0), NULL); - if (rcode != V7_OK) { - goto clean; - } - n++; - } - } - - if (n == 0) { - *res = V7_NULL; - goto clean; - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_replace(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - const char *s; - size_t s_len; - /* - * Buffer of temporary strings returned by the replacement funciton. Will be - * allocated below if only the replacement is a function. We need to store - * each string in a separate `val_t`, because string data of length <= 5 is - * stored right in `val_t`, so if there's more than one replacement, - * each subsequent replacement will overwrite the previous one. - */ - val_t *tmp_str_buf = NULL; - val_t out_str_o; - char *old_owned_mbuf_base = v7->owned_strings.buf; - char *old_owned_mbuf_end = v7->owned_strings.buf + v7->owned_strings.len; - - rcode = to_string(v7, this_obj, &this_obj, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - s = v7_get_string(v7, &this_obj, &s_len); - - if (s_len != 0 && v7_argc(v7) > 1) { - const char *const str_end = s + s_len; - char *p = (char *) s; - uint32_t out_sub_num = 0; - val_t ro = V7_UNDEFINED, str_func = V7_UNDEFINED; - struct slre_prog *prog; - struct slre_cap out_sub[V7_RE_MAX_REPL_SUB], *ptok = out_sub; - struct slre_loot loot; - int flag_g; - - rcode = obj_value_of(v7, v7_arg(v7, 0), &ro); - if (rcode != V7_OK) { - goto clean; - } - rcode = obj_value_of(v7, v7_arg(v7, 1), &str_func); - if (rcode != V7_OK) { - goto clean; - } - - if (!v7_is_regexp(v7, ro)) { - rcode = call_regex_ctor(v7, ro, &ro); - if (rcode != V7_OK) { - goto clean; - } - } - prog = v7_get_regexp_struct(v7, ro)->compiled_regexp; - flag_g = slre_get_flags(prog) & SLRE_FLAG_G; - - if (!v7_is_callable(v7, str_func)) { - rcode = to_string(v7, str_func, &str_func, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - } - - do { - int i; - if (slre_exec(prog, 0, p, str_end, &loot)) break; - if (p != loot.caps->start) { - ptok->start = p; - ptok->end = loot.caps->start; - ptok++; - out_sub_num++; - } - - if (v7_is_callable(v7, str_func)) { /* replace function */ - const char *rez_str; - size_t rez_len; - val_t arr = v7_mk_dense_array(v7); - - for (i = 0; i < loot.num_captures; i++) { - rcode = v7_array_push_throwing( - v7, arr, v7_mk_string(v7, loot.caps[i].start, - loot.caps[i].end - loot.caps[i].start, 1), - NULL); - if (rcode != V7_OK) { - goto clean; - } - } - rcode = v7_array_push_throwing( - v7, arr, v7_mk_number(v7, utfnlen(s, loot.caps[0].start - s)), - NULL); - if (rcode != V7_OK) { - goto clean; - } - - rcode = v7_array_push_throwing(v7, arr, this_obj, NULL); - if (rcode != V7_OK) { - goto clean; - } - - { - val_t val = V7_UNDEFINED; - - rcode = b_apply(v7, str_func, this_obj, arr, 0, &val); - if (rcode != V7_OK) { - goto clean; - } - - if (tmp_str_buf == NULL) { - tmp_str_buf = (val_t *) calloc(sizeof(val_t), V7_RE_MAX_REPL_SUB); - } - - rcode = to_string(v7, val, &tmp_str_buf[out_sub_num], NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - } - rez_str = v7_get_string(v7, &tmp_str_buf[out_sub_num], &rez_len); - if (rez_len) { - ptok->start = rez_str; - ptok->end = rez_str + rez_len; - ptok++; - out_sub_num++; - } - } else { /* replace string */ - struct slre_loot newsub; - size_t f_len; - const char *f_str = v7_get_string(v7, &str_func, &f_len); - slre_replace(&loot, s, s_len, f_str, f_len, &newsub); - for (i = 0; i < newsub.num_captures; i++) { - ptok->start = newsub.caps[i].start; - ptok->end = newsub.caps[i].end; - ptok++; - out_sub_num++; - } - } - p = (char *) loot.caps[0].end; - } while (flag_g && p < str_end); - if (p <= str_end) { - ptok->start = p; - ptok->end = str_end; - ptok++; - out_sub_num++; - } - out_str_o = v7_mk_string(v7, NULL, 0, 1); - ptok = out_sub; - do { - size_t ln = ptok->end - ptok->start; - const char *ps = ptok->start; - if (ptok->start >= old_owned_mbuf_base && - ptok->start < old_owned_mbuf_end) { - ps += v7->owned_strings.buf - old_owned_mbuf_base; - } - out_str_o = s_concat(v7, out_str_o, v7_mk_string(v7, ps, ln, 1)); - p += ln; - ptok++; - } while (--out_sub_num); - - *res = out_str_o; - goto clean; - } - - *res = this_obj; - -clean: - if (tmp_str_buf != NULL) { - free(tmp_str_buf); - tmp_str_buf = NULL; - } - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_search(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - long utf_shift = -1; - - if (v7_argc(v7) > 0) { - size_t s_len; - struct slre_loot sub; - val_t so = V7_UNDEFINED, ro = V7_UNDEFINED; - const char *s; - - rcode = obj_value_of(v7, v7_arg(v7, 0), &ro); - if (rcode != V7_OK) { - goto clean; - } - - if (!v7_is_regexp(v7, ro)) { - rcode = call_regex_ctor(v7, ro, &ro); - if (rcode != V7_OK) { - goto clean; - } - } - - rcode = to_string(v7, this_obj, &so, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - s = v7_get_string(v7, &so, &s_len); - - if (!slre_exec(v7_get_regexp_struct(v7, ro)->compiled_regexp, 0, s, - s + s_len, &sub)) - utf_shift = utfnlen(s, sub.caps[0].start - s); /* calc shift for UTF-8 */ - } else { - utf_shift = 0; - } - - *res = v7_mk_number(v7, utf_shift); - -clean: - return rcode; -} - -#endif /* V7_ENABLE__RegExp */ - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_slice(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - long from = 0, to = 0; - size_t len; - val_t so = V7_UNDEFINED; - const char *begin, *end; - int num_args = v7_argc(v7); - - rcode = to_string(v7, this_obj, &so, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - begin = v7_get_string(v7, &so, &len); - - to = len = utfnlen(begin, len); - if (num_args > 0) { - rcode = to_long(v7, v7_arg(v7, 0), 0, &from); - if (rcode != V7_OK) { - goto clean; - } - - if (from < 0) { - from += len; - if (from < 0) from = 0; - } else if ((size_t) from > len) - from = len; - if (num_args > 1) { - rcode = to_long(v7, v7_arg(v7, 1), 0, &to); - if (rcode != V7_OK) { - goto clean; - } - - if (to < 0) { - to += len; - if (to < 0) to = 0; - } else if ((size_t) to > len) - to = len; - } - } - - if (from > to) to = from; - end = utfnshift(begin, to); - begin = utfnshift(begin, from); - - *res = v7_mk_string(v7, begin, end - begin, 1); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -static enum v7_err s_transform(struct v7 *v7, val_t obj, Rune (*func)(Rune), - val_t *res) { - enum v7_err rcode = V7_OK; - val_t s = V7_UNDEFINED; - size_t i, n, len; - const char *p2, *p; - - rcode = to_string(v7, obj, &s, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - p = v7_get_string(v7, &s, &len); - - /* Pass NULL to make sure we're not creating dictionary value */ - *res = v7_mk_string(v7, NULL, len, 1); - - { - Rune r; - p2 = v7_get_string(v7, res, &len); - for (i = 0; i < len; i += n) { - n = chartorune(&r, p + i); - r = func(r); - runetochar((char *) p2 + i, &r); - } - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_toLowerCase(struct v7 *v7, v7_val_t *res) { - val_t this_obj = v7_get_this(v7); - return s_transform(v7, this_obj, tolowerrune, res); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_toUpperCase(struct v7 *v7, v7_val_t *res) { - val_t this_obj = v7_get_this(v7); - return s_transform(v7, this_obj, toupperrune, res); -} - -static int s_isspace(Rune c) { - return isspacerune(c) || isnewline(c); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_trim(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t s = V7_UNDEFINED; - size_t i, n, len, start = 0, end, state = 0; - const char *p; - Rune r; - - rcode = to_string(v7, this_obj, &s, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - p = v7_get_string(v7, &s, &len); - - end = len; - for (i = 0; i < len; i += n) { - n = chartorune(&r, p + i); - if (!s_isspace(r)) { - if (state++ == 0) start = i; - end = i + n; - } - } - - *res = v7_mk_string(v7, p + start, end - start, 1); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_length(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - size_t len = 0; - val_t s = V7_UNDEFINED; - - rcode = obj_value_of(v7, this_obj, &s); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_is_string(s)) { - const char *p = v7_get_string(v7, &s, &len); - len = utfnlen(p, len); - } - - *res = v7_mk_number(v7, len); -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_at(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - long arg0; - val_t s = V7_UNDEFINED; - - rcode = to_long(v7, v7_arg(v7, 0), -1, &arg0); - if (rcode != V7_OK) { - goto clean; - } - - rcode = obj_value_of(v7, this_obj, &s); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_is_string(s)) { - size_t n; - const unsigned char *p = (unsigned char *) v7_get_string(v7, &s, &n); - if (arg0 >= 0 && (size_t) arg0 < n) { - *res = v7_mk_number(v7, p[arg0]); - goto clean; - } - } - - *res = v7_mk_number(v7, NAN); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_blen(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - size_t len = 0; - val_t s = V7_UNDEFINED; - - rcode = obj_value_of(v7, this_obj, &s); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_is_string(s)) { - v7_get_string(v7, &s, &len); - } - - *res = v7_mk_number(v7, len); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -static enum v7_err s_substr(struct v7 *v7, val_t s, long start, long len, - val_t *res) { - enum v7_err rcode = V7_OK; - size_t n; - const char *p; - - rcode = to_string(v7, s, &s, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - p = v7_get_string(v7, &s, &n); - n = utfnlen(p, n); - - if (start < (long) n && len > 0) { - if (start < 0) start = (long) n + start; - if (start < 0) start = 0; - - if (start > (long) n) start = n; - if (len < 0) len = 0; - if (len > (long) n - start) len = n - start; - p = utfnshift(p, start); - } else { - len = 0; - } - - *res = v7_mk_string(v7, p, len, 1); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_substr(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - long start, len; - - rcode = to_long(v7, v7_arg(v7, 0), 0, &start); - if (rcode != V7_OK) { - goto clean; - } - - rcode = to_long(v7, v7_arg(v7, 1), LONG_MAX, &len); - if (rcode != V7_OK) { - goto clean; - } - - rcode = s_substr(v7, this_obj, start, len, res); - if (rcode != V7_OK) { - goto clean; - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_substring(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - long start, end; - - rcode = to_long(v7, v7_arg(v7, 0), 0, &start); - if (rcode != V7_OK) { - goto clean; - } - - rcode = to_long(v7, v7_arg(v7, 1), LONG_MAX, &end); - if (rcode != V7_OK) { - goto clean; - } - - if (start < 0) start = 0; - if (end < 0) end = 0; - if (start > end) { - long tmp = start; - start = end; - end = tmp; - } - - rcode = s_substr(v7, this_obj, start, end - start, res); - goto clean; - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Str_split(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - const char *s, *s_end; - size_t s_len; - long num_args = v7_argc(v7); - rcode = to_string(v7, this_obj, &this_obj, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - s = v7_get_string(v7, &this_obj, &s_len); - s_end = s + s_len; - - *res = v7_mk_dense_array(v7); - - if (num_args == 0) { - /* - * No arguments were given: resulting array will contain just a single - * element: the source string - */ - rcode = v7_array_push_throwing(v7, *res, this_obj, NULL); - if (rcode != V7_OK) { - goto clean; - } - } else { - val_t ro = V7_UNDEFINED; - long elem, limit; - size_t lookup_idx = 0, substr_idx = 0; - struct _str_split_ctx ctx; - - rcode = to_long(v7, v7_arg(v7, 1), LONG_MAX, &limit); - if (rcode != V7_OK) { - goto clean; - } - - rcode = obj_value_of(v7, v7_arg(v7, 0), &ro); - if (rcode != V7_OK) { - goto clean; - } - - /* Initialize substring context depending on the argument type */ - if (v7_is_regexp(v7, ro)) { -/* use RegExp implementation */ -#if V7_ENABLE__RegExp - ctx.p_init = subs_regexp_init; - ctx.p_exec = subs_regexp_exec; - ctx.p_add_caps = subs_regexp_split_add_caps; -#else - assert(0); -#endif - } else { - /* - * use String implementation: first of all, convert to String (if it's - * not already a String) - */ - rcode = to_string(v7, ro, &ro, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - ctx.p_init = subs_string_init; - ctx.p_exec = subs_string_exec; -#if V7_ENABLE__RegExp - ctx.p_add_caps = subs_string_split_add_caps; -#endif - } - /* initialize context */ - ctx.p_init(&ctx, v7, ro); - - if (s_len == 0) { - /* - * if `this` is (or converts to) an empty string, resulting array should - * contain empty string if only separator does not match an empty string. - * Otherwise, the array is left empty - */ - int matches_empty = !ctx.p_exec(&ctx, s, s); - if (!matches_empty) { - rcode = v7_array_push_throwing(v7, *res, this_obj, NULL); - if (rcode != V7_OK) { - goto clean; - } - } - } else { - size_t last_match_len = 0; - - for (elem = 0; elem < limit && lookup_idx < s_len;) { - size_t substr_len; - /* find next match, and break if there's no match */ - if (ctx.p_exec(&ctx, s + lookup_idx, s_end)) break; - - last_match_len = ctx.match_end - ctx.match_start; - substr_len = ctx.match_start - s - substr_idx; - - /* add next substring to the resulting array, if needed */ - if (substr_len > 0 || last_match_len > 0) { - rcode = v7_array_push_throwing( - v7, *res, v7_mk_string(v7, s + substr_idx, substr_len, 1), NULL); - if (rcode != V7_OK) { - goto clean; - } - elem++; - -#if V7_ENABLE__RegExp - /* Add captures (for RegExp only) */ - elem = ctx.p_add_caps(&ctx, *res, elem, limit); -#endif - } - - /* advance lookup_idx appropriately */ - if (last_match_len == 0) { - /* empty match: advance to the next char */ - const char *next = utfnshift((s + lookup_idx), 1); - lookup_idx += (next - (s + lookup_idx)); - } else { - /* non-empty match: advance to the end of match */ - lookup_idx = ctx.match_end - s; - } - - /* - * always remember the end of the match, so that next time we will take - * substring from that position - */ - substr_idx = ctx.match_end - s; - } - - /* add the last substring to the resulting array, if needed */ - if (elem < limit) { - size_t substr_len = s_len - substr_idx; - if (substr_len > 0 || last_match_len > 0) { - rcode = v7_array_push_throwing( - v7, *res, v7_mk_string(v7, s + substr_idx, substr_len, 1), NULL); - if (rcode != V7_OK) { - goto clean; - } - } - } - } - } - -clean: - return rcode; -} - -V7_PRIVATE void init_string(struct v7 *v7) { - val_t str = mk_cfunction_obj_with_proto(v7, String_ctor, 1, - v7->vals.string_prototype); - v7_def(v7, v7->vals.global_object, "String", 6, V7_DESC_ENUMERABLE(0), str); - - set_cfunc_prop(v7, str, "fromCharCode", Str_fromCharCode); - set_cfunc_prop(v7, v7->vals.string_prototype, "charCodeAt", Str_charCodeAt); - set_cfunc_prop(v7, v7->vals.string_prototype, "charAt", Str_charAt); - set_cfunc_prop(v7, v7->vals.string_prototype, "concat", Str_concat); - set_cfunc_prop(v7, v7->vals.string_prototype, "indexOf", Str_indexOf); - set_cfunc_prop(v7, v7->vals.string_prototype, "substr", Str_substr); - set_cfunc_prop(v7, v7->vals.string_prototype, "substring", Str_substring); - set_cfunc_prop(v7, v7->vals.string_prototype, "valueOf", Str_valueOf); - set_cfunc_prop(v7, v7->vals.string_prototype, "lastIndexOf", Str_lastIndexOf); -#if V7_ENABLE__String__localeCompare - set_cfunc_prop(v7, v7->vals.string_prototype, "localeCompare", - Str_localeCompare); -#endif -#if V7_ENABLE__RegExp - set_cfunc_prop(v7, v7->vals.string_prototype, "match", Str_match); - set_cfunc_prop(v7, v7->vals.string_prototype, "replace", Str_replace); - set_cfunc_prop(v7, v7->vals.string_prototype, "search", Str_search); -#endif - set_cfunc_prop(v7, v7->vals.string_prototype, "split", Str_split); - set_cfunc_prop(v7, v7->vals.string_prototype, "slice", Str_slice); - set_cfunc_prop(v7, v7->vals.string_prototype, "trim", Str_trim); - set_cfunc_prop(v7, v7->vals.string_prototype, "toLowerCase", Str_toLowerCase); -#if V7_ENABLE__String__localeLowerCase - set_cfunc_prop(v7, v7->vals.string_prototype, "toLocaleLowerCase", - Str_toLowerCase); -#endif - set_cfunc_prop(v7, v7->vals.string_prototype, "toUpperCase", Str_toUpperCase); -#if V7_ENABLE__String__localeUpperCase - set_cfunc_prop(v7, v7->vals.string_prototype, "toLocaleUpperCase", - Str_toUpperCase); -#endif - set_cfunc_prop(v7, v7->vals.string_prototype, "toString", Str_toString); - - v7_def(v7, v7->vals.string_prototype, "length", 6, V7_DESC_GETTER(1), - v7_mk_cfunction(Str_length)); - - /* Non-standard Cesanta extension */ - set_cfunc_prop(v7, v7->vals.string_prototype, "at", Str_at); - v7_def(v7, v7->vals.string_prototype, "blen", 4, V7_DESC_GETTER(1), - v7_mk_cfunction(Str_blen)); -} -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_date.c" -#endif -/* - * Copyright (c) 2015 Cesanta Software Limited - * All rights reserved - */ - -/* Amalgamated: #include "v7/src/internal.h" */ -/* Amalgamated: #include "common/str_util.h" */ -/* Amalgamated: #include "v7/src/std_object.h" */ -/* Amalgamated: #include "v7/src/core.h" */ -/* Amalgamated: #include "v7/src/util.h" */ -/* Amalgamated: #include "v7/src/function.h" */ -/* Amalgamated: #include "v7/src/object.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "v7/src/exceptions.h" */ -/* Amalgamated: #include "v7/src/primitive.h" */ -/* Amalgamated: #include "v7/src/string.h" */ - -#if V7_ENABLE__Date - -#include <locale.h> -#include <time.h> - -#ifndef _WIN32 -extern long timezone; -#include <sys/time.h> -#endif - -#if defined(_MSC_VER) -#define timezone _timezone -#define tzname _tzname -#if _MSC_VER >= 1800 -#define tzset _tzset -#endif -#endif - -#if defined(__cplusplus) -extern "C" { -#endif /* __cplusplus */ - -typedef double etime_t; /* double is suitable type for ECMA time */ -/* inernally we have to use 64-bit integer for some operations */ -typedef int64_t etimeint_t; -#define INVALID_TIME NAN - -#define msPerDay 86400000 -#define HoursPerDay 24 -#define MinutesPerHour 60 -#define SecondsPerMinute 60 -#define SecondsPerHour 3600 -#define msPerSecond 1000 -#define msPerMinute 60000 -#define msPerHour 3600000 -#define MonthsInYear 12 - -/* ECMA alternative to struct tm */ -struct timeparts { - int year; /* can be negative, up to +-282000 */ - int month; /* 0-11 */ - int day; /* 1-31 */ - int hour; /* 0-23 */ - int min; /* 0-59 */ - int sec; /* 0-59 */ - int msec; - int dayofweek; /* 0-6 */ -}; - -static etimeint_t g_gmtoffms; /* timezone offset, ms, no DST */ -static const char *g_tzname; /* current timezone name */ - -/* Leap year formula copied from ECMA 5.1 standart as is */ -static int ecma_DaysInYear(int y) { - if (y % 4 != 0) { - return 365; - } else if (y % 4 == 0 && y % 100 != 0) { - return 366; - } else if (y % 100 == 0 && y % 400 != 0) { - return 365; - } else if (y % 400 == 0) { - return 366; - } else { - return 365; - } -} - -static etimeint_t ecma_DayFromYear(etimeint_t y) { - return 365 * (y - 1970) + floor((y - 1969) / 4) - floor((y - 1901) / 100) + - floor((y - 1601) / 400); -} - -static etimeint_t ecma_TimeFromYear(etimeint_t y) { - return msPerDay * ecma_DayFromYear(y); -} - -static int ecma_IsLeapYear(int year) { - return ecma_DaysInYear(year) == 366; -} - -static int *ecma_getfirstdays(int isleap) { - static int sdays[2][MonthsInYear + 1] = { - {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, - {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}}; - - return sdays[isleap]; -} - -static int ecma_DaylightSavingTA(etime_t t) { - time_t time = t / 1000; - /* - * Win32 doesn't have locatime_r - * nixes don't have localtime_s - * as result using localtime - */ - struct tm *tm = localtime(&time); - if (tm == NULL) { - /* doesn't work on windows for times before epoch */ - return 0; - } - if (tm->tm_isdst > 0) { - return msPerHour; - } else { - return 0; - } -} - -static int ecma_LocalTZA(void) { - return (int) -g_gmtoffms; -} - -static etimeint_t ecma_UTC(etime_t t) { - return t - ecma_LocalTZA() - ecma_DaylightSavingTA(t - ecma_LocalTZA()); -} - -#if V7_ENABLE__Date__toString || V7_ENABLE__Date__toLocaleString || \ - V7_ENABLE__Date__toJSON || V7_ENABLE__Date__getters || \ - V7_ENABLE__Date__setters -static int ecma_YearFromTime_s(etime_t t) { - int first = floor((t / msPerDay) / 366) + 1970, - last = floor((t / msPerDay) / 365) + 1970, middle = 0; - - if (last < first) { - int temp = first; - first = last; - last = temp; - } - - while (last > first) { - middle = (last + first) / 2; - if (ecma_TimeFromYear(middle) > t) { - last = middle - 1; - } else { - if (ecma_TimeFromYear(middle) <= t) { - if (ecma_TimeFromYear(middle + 1) > t) { - first = middle; - break; - } - first = middle + 1; - } - } - } - - return first; -} - -static etimeint_t ecma_Day(etime_t t) { - return floor(t / msPerDay); -} - -static int ecma_DayWithinYear(etime_t t, int year) { - return (int) (ecma_Day(t) - ecma_DayFromYear(year)); -} - -static int ecma_MonthFromTime(etime_t t, int year) { - int *days, i; - etimeint_t dwy = ecma_DayWithinYear(t, year); - - days = ecma_getfirstdays(ecma_IsLeapYear(year)); - - for (i = 0; i < MonthsInYear; i++) { - if (dwy >= days[i] && dwy < days[i + 1]) { - return i; - } - } - - return -1; -} - -static int ecma_DateFromTime(etime_t t, int year) { - int *days, mft = ecma_MonthFromTime(t, year), - dwy = ecma_DayWithinYear(t, year); - - if (mft > 11) { - return -1; - } - - days = ecma_getfirstdays(ecma_IsLeapYear(year)); - - return dwy - days[mft] + 1; -} - -#define DEF_EXTRACT_TIMEPART(funcname, c1, c2) \ - static int ecma_##funcname(etime_t t) { \ - int ret = (etimeint_t) floor(t / c1) % c2; \ - if (ret < 0) { \ - ret += c2; \ - } \ - return ret; \ - } - -DEF_EXTRACT_TIMEPART(HourFromTime, msPerHour, HoursPerDay) -DEF_EXTRACT_TIMEPART(MinFromTime, msPerMinute, MinutesPerHour) -DEF_EXTRACT_TIMEPART(SecFromTime, msPerSecond, SecondsPerMinute) -DEF_EXTRACT_TIMEPART(msFromTime, 1, msPerSecond) - -static int ecma_WeekDay(etime_t t) { - int ret = (ecma_Day(t) + 4) % 7; - if (ret < 0) { - ret += 7; - } - - return ret; -} - -static void d_gmtime(const etime_t *t, struct timeparts *tp) { - tp->year = ecma_YearFromTime_s(*t); - tp->month = ecma_MonthFromTime(*t, tp->year); - tp->day = ecma_DateFromTime(*t, tp->year); - tp->hour = ecma_HourFromTime(*t); - tp->min = ecma_MinFromTime(*t); - tp->sec = ecma_SecFromTime(*t); - tp->msec = ecma_msFromTime(*t); - tp->dayofweek = ecma_WeekDay(*t); -} -#endif /* V7_ENABLE__Date__toString || V7_ENABLE__Date__toLocaleString || \ - V7_ENABLE__Date__getters || V7_ENABLE__Date__setters */ - -#if V7_ENABLE__Date__toString || V7_ENABLE__Date__toLocaleString || \ - V7_ENABLE__Date__getters || V7_ENABLE__Date__setters -static etimeint_t ecma_LocalTime(etime_t t) { - return t + ecma_LocalTZA() + ecma_DaylightSavingTA(t); -} - -static void d_localtime(const etime_t *time, struct timeparts *tp) { - etime_t local_time = ecma_LocalTime(*time); - d_gmtime(&local_time, tp); -} -#endif - -static etimeint_t ecma_MakeTime(etimeint_t hour, etimeint_t min, etimeint_t sec, - etimeint_t ms) { - return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * - msPerSecond + - ms; -} - -static etimeint_t ecma_MakeDay(int year, int month, int date) { - int *days; - etimeint_t yday, mday; - - year += floor(month / 12); - month = month % 12; - yday = floor(ecma_TimeFromYear(year) / msPerDay); - days = ecma_getfirstdays(ecma_IsLeapYear(year)); - mday = days[month]; - - return yday + mday + date - 1; -} - -static etimeint_t ecma_MakeDate(etimeint_t day, etimeint_t time) { - return (day * msPerDay + time); -} - -static void d_gettime(etime_t *t) { -#ifndef _WIN32 - struct timeval tv; - gettimeofday(&tv, NULL); - *t = (etime_t) tv.tv_sec * 1000 + (etime_t) tv.tv_usec / 1000; -#else - /* TODO(mkm): use native windows API in order to get ms granularity */ - *t = time(NULL) * 1000.0; -#endif -} - -static etime_t d_mktime_impl(const struct timeparts *tp) { - return ecma_MakeDate(ecma_MakeDay(tp->year, tp->month, tp->day), - ecma_MakeTime(tp->hour, tp->min, tp->sec, tp->msec)); -} - -#if V7_ENABLE__Date__setters -static etime_t d_lmktime(const struct timeparts *tp) { - return ecma_UTC(d_mktime_impl(tp)); -} -#endif - -static etime_t d_gmktime(const struct timeparts *tp) { - return d_mktime_impl(tp); -} - -typedef etime_t (*fmaketime_t)(const struct timeparts *); -typedef void (*fbreaktime_t)(const etime_t *, struct timeparts *); - -#if V7_ENABLE__Date__toString || V7_ENABLE__Date__toLocaleString || \ - V7_ENABLE__Date__toJSON -static val_t d_trytogetobjforstring(struct v7 *v7, val_t obj) { - enum v7_err rcode = V7_OK; - val_t ret = V7_UNDEFINED; - - rcode = obj_value_of(v7, obj, &ret); - if (rcode != V7_OK) { - goto clean; - } - - if (ret == V7_TAG_NAN) { - rcode = v7_throwf(v7, TYPE_ERROR, "Date is invalid (for string)"); - goto clean; - } - -clean: - (void) rcode; - return ret; -} -#endif - -#if V7_ENABLE__Date__parse || V7_ENABLE__Date__UTC -static int d_iscalledasfunction(struct v7 *v7, val_t this_obj) { - /* TODO(alashkin): verify this statement */ - return is_prototype_of(v7, this_obj, v7->vals.date_prototype); -} -#endif - -static const char *mon_name[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - -int d_getnumbyname(const char **arr, int arr_size, const char *str) { - int i; - for (i = 0; i < arr_size; i++) { - if (strncmp(arr[i], str, 3) == 0) { - return i + 1; - } - } - - return -1; -} - -int date_parse(const char *str, int *a1, int *a2, int *a3, char sep, - char *rest) { - char frmDate[] = " %d/%d/%d%[^\0]"; - frmDate[3] = frmDate[6] = sep; - return sscanf(str, frmDate, a1, a2, a3, rest); -} - -#define NO_TZ 0x7FFFFFFF - -/* - * not very smart but simple, and working according - * to ECMA5.1 StringToDate function - */ -static int d_parsedatestr(const char *jstr, size_t len, struct timeparts *tp, - int *tz) { - char gmt[4]; - char buf1[100] = {0}, buf2[100] = {0}; - int res = 0; - char str[101]; - memcpy(str, jstr, len); - str[len] = '\0'; - memset(tp, 0, sizeof(*tp)); - *tz = NO_TZ; - - /* trying toISOSrting() format */ - { - const char *frmISOString = " %d-%02d-%02dT%02d:%02d:%02d.%03dZ"; - res = sscanf(str, frmISOString, &tp->year, &tp->month, &tp->day, &tp->hour, - &tp->min, &tp->sec, &tp->msec); - if (res == 7) { - *tz = 0; - return 1; - } - } - - /* trying toString()/toUTCString()/toDateFormat() formats */ - { - char month[4]; - int dowlen; - const char *frmString = " %*s%n %03s %02d %d %02d:%02d:%02d %03s%d"; - res = sscanf(str, frmString, &dowlen, month, &tp->day, &tp->year, &tp->hour, - &tp->min, &tp->sec, gmt, tz); - if ((res == 3 || (res >= 6 && res <= 8)) && dowlen == 3) { - if ((tp->month = d_getnumbyname(mon_name, ARRAY_SIZE(mon_name), month)) != - -1) { - if (res == 7 && strncmp(gmt, "GMT", 3) == 0) { - *tz = 0; - } - return 1; - } - } - } - - /* trying the rest */ - - /* trying date */ - - if (!(date_parse(str, &tp->month, &tp->day, &tp->year, '/', buf1) >= 3 || - date_parse(str, &tp->day, &tp->month, &tp->year, '.', buf1) >= 3 || - date_parse(str, &tp->year, &tp->month, &tp->day, '-', buf1) >= 3)) { - return 0; - } - - /* there is date, trying time; from here return 0 only on errors */ - - /* trying HH:mm */ - { - const char *frmMMhh = " %d:%d%[^\0]"; - res = sscanf(buf1, frmMMhh, &tp->hour, &tp->min, buf2); - /* can't get time, but have some symbols, assuming error */ - if (res < 2) { - return (strlen(buf2) == 0); - } - } - - /* trying seconds */ - { - const char *frmss = ":%d%[^\0]"; - memset(buf1, 0, sizeof(buf1)); - res = sscanf(buf2, frmss, &tp->sec, buf1); - } - - /* even if we don't get seconds we gonna try to get tz */ - { - char *rest = res ? buf1 : buf2; - char *buf = res ? buf2 : buf1; - const char *frmtz = " %03s%d%[^\0]"; - - res = sscanf(rest, frmtz, gmt, tz, buf); - if (res == 1 && strncmp(gmt, "GMT", 3) == 0) { - *tz = 0; - } - } - - /* return OK if we are at the end of string */ - return res <= 2; -} - -static int d_timeFromString(etime_t *time, const char *str, size_t str_len) { - struct timeparts tp; - int tz; - - *time = INVALID_TIME; - - if (str_len > 100) { - /* too long for valid date string */ - return 0; - } - - if (d_parsedatestr(str, str_len, &tp, &tz)) { - /* check results */ - int valid = 0; - - tp.month--; - valid = tp.day >= 1 && tp.day <= 31; - valid &= tp.month >= 0 && tp.month <= 11; - valid &= tp.hour >= 0 && tp.hour <= 23; - valid &= tp.min >= 0 && tp.min <= 59; - valid &= tp.sec >= 0 && tp.sec <= 59; - - if (tz != NO_TZ && tz > 12) { - tz /= 100; - } - - valid &= (abs(tz) <= 12 || tz == NO_TZ); - - if (valid) { - *time = d_gmktime(&tp); - - if (tz != NO_TZ) { - /* timezone specified, use it */ - *time -= (tz * msPerHour); - } else if (tz != 0) { - /* assuming local timezone and moving back to UTC */ - *time = ecma_UTC(*time); - } - } - } - - return !isnan(*time); -} - -/* notice: holding month in calendar format (1-12, not 0-11) */ -struct dtimepartsarr { - etime_t args[7]; -}; - -enum detimepartsarr { - tpyear = 0, - tpmonth, - tpdate, - tphours, - tpminutes, - tpseconds, - tpmsec, - tpmax -}; - -static etime_t d_changepartoftime(const etime_t *current, - struct dtimepartsarr *a, - fbreaktime_t breaktimefunc, - fmaketime_t maketimefunc) { - /* - * 0 = year, 1 = month , 2 = date , 3 = hours, - * 4 = minutes, 5 = seconds, 6 = ms - */ - struct timeparts tp; - unsigned long i; - int *tp_arr[7]; - /* - * C89 doesn't allow initialization - * like x = {&tp.year, &tp.month, .... } - */ - tp_arr[0] = &tp.year; - tp_arr[1] = &tp.month; - tp_arr[2] = &tp.day; - tp_arr[3] = &tp.hour; - tp_arr[4] = &tp.min; - tp_arr[5] = &tp.sec; - tp_arr[6] = &tp.msec; - - memset(&tp, 0, sizeof(tp)); - - if (breaktimefunc != NULL) { - breaktimefunc(current, &tp); - } - - for (i = 0; i < ARRAY_SIZE(tp_arr); i++) { - if (!isnan(a->args[i]) && !isinf(a->args[i])) { - *tp_arr[i] = (int) a->args[i]; - } - } - - return maketimefunc(&tp); -} - -#if V7_ENABLE__Date__setters || V7_ENABLE__Date__UTC -static etime_t d_time_number_from_arr(struct v7 *v7, int start_pos, - fbreaktime_t breaktimefunc, - fmaketime_t maketimefunc) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - etime_t ret_time = INVALID_TIME; - long cargs; - - val_t objtime = V7_UNDEFINED; - rcode = obj_value_of(v7, this_obj, &objtime); - if (rcode != V7_OK) { - goto clean; - } - - if ((cargs = v7_argc(v7)) >= 1 && objtime != V7_TAG_NAN) { - int i; - etime_t new_part = INVALID_TIME; - struct dtimepartsarr a; - for (i = 0; i < 7; i++) { - a.args[i] = INVALID_TIME; - } - - for (i = 0; i < cargs && (i + start_pos < tpmax); i++) { - { - val_t arg = v7_arg(v7, i); - rcode = to_number_v(v7, arg, &arg); - if (rcode != V7_OK) { - goto clean; - } - new_part = v7_get_double(v7, arg); - } - - if (isnan(new_part)) { - break; - } - - a.args[i + start_pos] = new_part; - } - - if (!isnan(new_part)) { - etime_t current_time = v7_get_double(v7, objtime); - ret_time = - d_changepartoftime(¤t_time, &a, breaktimefunc, maketimefunc); - } - } - -clean: - (void) rcode; - return ret_time; -} -#endif /* V7_ENABLE__Date__setters */ - -#if V7_ENABLE__Date__toString -static int d_tptostr(const struct timeparts *tp, char *buf, int addtz); -#endif - -/* constructor */ -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Date_ctor(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - etime_t ret_time = INVALID_TIME; - if (v7_is_generic_object(this_obj) && this_obj != v7->vals.global_object) { - long cargs = v7_argc(v7); - if (cargs <= 0) { - /* no parameters - return current date & time */ - d_gettime(&ret_time); - } else if (cargs == 1) { - /* one parameter */ - val_t arg = v7_arg(v7, 0); - if (v7_is_string(arg)) { /* it could be string */ - size_t str_size; - const char *str = v7_get_string(v7, &arg, &str_size); - d_timeFromString(&ret_time, str, str_size); - } - if (isnan(ret_time)) { - /* - * if cannot be parsed or - * not string at all - trying to convert to number - */ - ret_time = 0; - rcode = to_number_v(v7, arg, &arg); - if (rcode != V7_OK) { - goto clean; - } - ret_time = v7_get_double(v7, arg); - if (rcode != V7_OK) { - goto clean; - } - } - } else { - /* 2+ paramaters - should be parts of a date */ - struct dtimepartsarr a; - int i; - - memset(&a, 0, sizeof(a)); - - for (i = 0; i < cargs; i++) { - val_t arg = v7_arg(v7, i); - rcode = to_number_v(v7, arg, &arg); - if (rcode != V7_OK) { - goto clean; - } - a.args[i] = v7_get_double(v7, arg); - if (isnan(a.args[i])) { - break; - } - } - - if (i >= cargs) { - /* - * If date is supplied then let - * dt be ToNumber(date); else let dt be 1. - */ - if (a.args[tpdate] == 0) { - a.args[tpdate] = 1; - } - - if (a.args[tpyear] >= 0 && a.args[tpyear] <= 99) { - /* - * If y is not NaN and 0 <= ToInteger(y) <= 99, - * then let yr be 1900+ToInteger(y); otherwise, let yr be y. - */ - a.args[tpyear] += 1900; - } - - ret_time = ecma_UTC(d_changepartoftime(0, &a, 0, d_gmktime)); - } - } - - obj_prototype_set(v7, get_object_struct(this_obj), - get_object_struct(v7->vals.date_prototype)); - - v7_def(v7, this_obj, "", 0, _V7_DESC_HIDDEN(1), v7_mk_number(v7, ret_time)); - /* - * implicitly returning `this`: `call_cfunction()` in bcode.c will do - * that for us - */ - goto clean; - } else { - /* - * according to 15.9.2.1 we should ignore all - * parameters in case of function-call - */ - char buf[50]; - int len; - -#if V7_ENABLE__Date__toString - struct timeparts tp; - d_gettime(&ret_time); - d_localtime(&ret_time, &tp); - len = d_tptostr(&tp, buf, 1); -#else - len = 0; -#endif /* V7_ENABLE__Date__toString */ - - *res = v7_mk_string(v7, buf, len, 1); - goto clean; - } - -clean: - return rcode; -} - -#if V7_ENABLE__Date__toString || V7_ENABLE__Date__toJSON -static int d_timetoISOstr(const etime_t *time, char *buf, size_t buf_size) { - /* ISO format: "+XXYYYY-MM-DDTHH:mm:ss.sssZ"; */ - struct timeparts tp; - char use_ext = 0; - const char *ey_frm = "%06d-%02d-%02dT%02d:%02d:%02d.%03dZ"; - const char *simpl_frm = "%d-%02d-%02dT%02d:%02d:%02d.%03dZ"; - - d_gmtime(time, &tp); - - if (abs(tp.year) > 9999 || tp.year < 0) { - *buf = (tp.year > 0) ? '+' : '-'; - use_ext = 1; - } - - return c_snprintf(buf + use_ext, buf_size - use_ext, - use_ext ? ey_frm : simpl_frm, abs(tp.year), tp.month + 1, - tp.day, tp.hour, tp.min, tp.sec, tp.msec) + - use_ext; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Date_toISOString(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - char buf[30]; - etime_t time; - int len; - - if (val_type(v7, this_obj) != V7_TYPE_DATE_OBJECT) { - rcode = v7_throwf(v7, TYPE_ERROR, "This is not a Date object"); - goto clean; - } - - time = v7_get_double(v7, d_trytogetobjforstring(v7, this_obj)); - len = d_timetoISOstr(&time, buf, sizeof(buf)); - if (len > (int) (sizeof(buf) - 1 /*null-term*/)) { - len = (int) (sizeof(buf) - 1 /*null-term*/); - } - - *res = v7_mk_string(v7, buf, len, 1); - -clean: - return rcode; -} -#endif /* V7_ENABLE__Date__toString || V7_ENABLE__Date__toJSON */ - -#if V7_ENABLE__Date__toString -typedef int (*ftostring_t)(const struct timeparts *, char *, int); - -WARN_UNUSED_RESULT -static enum v7_err d_tostring(struct v7 *v7, val_t obj, - fbreaktime_t breaktimefunc, - ftostring_t tostringfunc, int addtz, - v7_val_t *res) { - enum v7_err rcode = V7_OK; - struct timeparts tp; - int len; - char buf[100]; - etime_t time; - - time = v7_get_double(v7, d_trytogetobjforstring(v7, obj)); - - breaktimefunc(&time, &tp); - len = tostringfunc(&tp, buf, addtz); - - *res = v7_mk_string(v7, buf, len, 1); - return rcode; -} - -/* using macros to avoid copy-paste technic */ -#define DEF_TOSTR(funcname, breaktimefunc, tostrfunc, addtz) \ - WARN_UNUSED_RESULT \ - V7_PRIVATE enum v7_err Date_to##funcname(struct v7 *v7, v7_val_t *res) { \ - val_t this_obj = v7_get_this(v7); \ - return d_tostring(v7, this_obj, breaktimefunc, tostrfunc, addtz, res); \ - } - -/* non-locale function should always return in english and 24h-format */ -static const char *wday_name[] = {"Sun", "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat"}; - -static int d_tptodatestr(const struct timeparts *tp, char *buf, int addtz) { - (void) addtz; - - return sprintf(buf, "%s %s %02d %d", wday_name[tp->dayofweek], - mon_name[tp->month], tp->day, tp->year); -} - -DEF_TOSTR(DateString, d_localtime, d_tptodatestr, 1) - -static const char *d_gettzname(void) { - return g_tzname; -} - -static int d_tptotimestr(const struct timeparts *tp, char *buf, int addtz) { - int len; - - len = sprintf(buf, "%02d:%02d:%02d GMT", tp->hour, tp->min, tp->sec); - - if (addtz && g_gmtoffms != 0) { - len = sprintf(buf + len, "%c%02d00 (%s)", g_gmtoffms > 0 ? '-' : '+', - abs((int) g_gmtoffms / msPerHour), d_gettzname()); - } - - return (int) strlen(buf); -} - -DEF_TOSTR(TimeString, d_localtime, d_tptotimestr, 1) - -static int d_tptostr(const struct timeparts *tp, char *buf, int addtz) { - int len = d_tptodatestr(tp, buf, addtz); - *(buf + len) = ' '; - return d_tptotimestr(tp, buf + len + 1, addtz) + len + 1; -} - -DEF_TOSTR(String, d_localtime, d_tptostr, 1) -DEF_TOSTR(UTCString, d_gmtime, d_tptostr, 0) -#endif /* V7_ENABLE__Date__toString */ - -#if V7_ENABLE__Date__toLocaleString -struct d_locale { - char locale[50]; -}; - -static void d_getcurrentlocale(struct d_locale *loc) { - strcpy(loc->locale, setlocale(LC_TIME, 0)); -} - -static void d_setlocale(const struct d_locale *loc) { - setlocale(LC_TIME, loc ? loc->locale : ""); -} - -/* TODO(alashkin): check portability */ -WARN_UNUSED_RESULT -static enum v7_err d_tolocalestr(struct v7 *v7, val_t obj, const char *frm, - v7_val_t *res) { - enum v7_err rcode = V7_OK; - char buf[250]; - size_t len; - struct tm t; - etime_t time; - struct d_locale prev_locale; - struct timeparts tp; - - time = v7_get_double(v7, d_trytogetobjforstring(v7, obj)); - - d_getcurrentlocale(&prev_locale); - d_setlocale(0); - d_localtime(&time, &tp); - - memset(&t, 0, sizeof(t)); - t.tm_year = tp.year - 1900; - t.tm_mon = tp.month; - t.tm_mday = tp.day; - t.tm_hour = tp.hour; - t.tm_min = tp.min; - t.tm_sec = tp.sec; - t.tm_wday = tp.dayofweek; - - len = strftime(buf, sizeof(buf), frm, &t); - - d_setlocale(&prev_locale); - - *res = v7_mk_string(v7, buf, len, 1); - return rcode; -} - -#define DEF_TOLOCALESTR(funcname, frm) \ - WARN_UNUSED_RESULT \ - V7_PRIVATE enum v7_err Date_to##funcname(struct v7 *v7, v7_val_t *res) { \ - val_t this_obj = v7_get_this(v7); \ - return d_tolocalestr(v7, this_obj, frm, res); \ - } - -DEF_TOLOCALESTR(LocaleString, "%c") -DEF_TOLOCALESTR(LocaleDateString, "%x") -DEF_TOLOCALESTR(LocaleTimeString, "%X") -#endif /* V7_ENABLE__Date__toLocaleString */ - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Date_valueOf(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - if (!v7_is_generic_object(this_obj) || - (v7_is_generic_object(this_obj) && - v7_get_proto(v7, this_obj) != v7->vals.date_prototype)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Date.valueOf called on non-Date object"); - goto clean; - } - - rcode = Obj_valueOf(v7, res); - if (rcode != V7_OK) { - goto clean; - } - -clean: - return rcode; -} - -#if V7_ENABLE__Date__getters -static struct timeparts *d_getTimePart(struct v7 *v7, val_t val, - struct timeparts *tp, - fbreaktime_t breaktimefunc) { - etime_t time; - time = v7_get_double(v7, val); - breaktimefunc(&time, tp); - return tp; -} - -#define DEF_GET_TP_FUNC(funcName, tpmember, breaktimefunc) \ - WARN_UNUSED_RESULT \ - V7_PRIVATE enum v7_err Date_get##funcName(struct v7 *v7, v7_val_t *res) { \ - enum v7_err rcode = V7_OK; \ - val_t v = V7_UNDEFINED; \ - struct timeparts tp; \ - val_t this_obj = v7_get_this(v7); \ - \ - rcode = obj_value_of(v7, this_obj, &v); \ - if (rcode != V7_OK) { \ - goto clean; \ - } \ - *res = v7_mk_number( \ - v7, v == V7_TAG_NAN ? NAN : d_getTimePart(v7, v, &tp, breaktimefunc) \ - ->tpmember); \ - clean: \ - return rcode; \ - } - -#define DEF_GET_TP(funcName, tpmember) \ - DEF_GET_TP_FUNC(UTC##funcName, tpmember, d_gmtime) \ - DEF_GET_TP_FUNC(funcName, tpmember, d_localtime) - -DEF_GET_TP(Date, day) -DEF_GET_TP(FullYear, year) -DEF_GET_TP(Month, month) -DEF_GET_TP(Hours, hour) -DEF_GET_TP(Minutes, min) -DEF_GET_TP(Seconds, sec) -DEF_GET_TP(Milliseconds, msec) -DEF_GET_TP(Day, dayofweek) - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Date_getTime(struct v7 *v7, v7_val_t *res) { - return Date_valueOf(v7, res); -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Date_getTimezoneOffset(struct v7 *v7, v7_val_t *res) { - (void) v7; - *res = v7_mk_number(v7, g_gmtoffms / msPerMinute); - return V7_OK; -} -#endif /* V7_ENABLE__Date__getters */ - -#if V7_ENABLE__Date__setters -WARN_UNUSED_RESULT -static enum v7_err d_setTimePart(struct v7 *v7, int start_pos, - fbreaktime_t breaktimefunc, - fmaketime_t maketimefunc, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - etime_t ret_time = - d_time_number_from_arr(v7, start_pos, breaktimefunc, maketimefunc); - - *res = v7_mk_number(v7, ret_time); - v7_def(v7, this_obj, "", 0, _V7_DESC_HIDDEN(1), *res); - - return rcode; -} - -#define DEF_SET_TP(name, start_pos) \ - WARN_UNUSED_RESULT \ - V7_PRIVATE enum v7_err Date_setUTC##name(struct v7 *v7, v7_val_t *res) { \ - return d_setTimePart(v7, start_pos, d_gmtime, d_gmktime, res); \ - } \ - WARN_UNUSED_RESULT \ - V7_PRIVATE enum v7_err Date_set##name(struct v7 *v7, v7_val_t *res) { \ - return d_setTimePart(v7, start_pos, d_localtime, d_lmktime, res); \ - } - -DEF_SET_TP(Milliseconds, tpmsec) -DEF_SET_TP(Seconds, tpseconds) -DEF_SET_TP(Minutes, tpminutes) -DEF_SET_TP(Hours, tphours) -DEF_SET_TP(Date, tpdate) -DEF_SET_TP(Month, tpmonth) -DEF_SET_TP(FullYear, tpyear) - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Date_setTime(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - - if (v7_argc(v7) >= 1) { - rcode = to_number_v(v7, v7_arg(v7, 0), res); - if (rcode != V7_OK) { - goto clean; - } - } - - v7_def(v7, this_obj, "", 0, _V7_DESC_HIDDEN(1), *res); - -clean: - return rcode; -} -#endif /* V7_ENABLE__Date__setters */ - -#if V7_ENABLE__Date__toJSON -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Date_toJSON(struct v7 *v7, v7_val_t *res) { - return Date_toISOString(v7, res); -} -#endif /* V7_ENABLE__Date__toJSON */ - -#if V7_ENABLE__Date__now -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Date_now(struct v7 *v7, v7_val_t *res) { - etime_t ret_time; - (void) v7; - - d_gettime(&ret_time); - - *res = v7_mk_number(v7, ret_time); - return V7_OK; -} -#endif /* V7_ENABLE__Date__now */ - -#if V7_ENABLE__Date__parse -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Date_parse(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - etime_t ret_time = INVALID_TIME; - - if (!d_iscalledasfunction(v7, this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Date.parse() called on object"); - goto clean; - } - - if (v7_argc(v7) >= 1) { - val_t arg0 = v7_arg(v7, 0); - if (v7_is_string(arg0)) { - size_t size; - const char *time_str = v7_get_string(v7, &arg0, &size); - - d_timeFromString(&ret_time, time_str, size); - } - } - - *res = v7_mk_number(v7, ret_time); - -clean: - return rcode; -} -#endif /* V7_ENABLE__Date__parse */ - -#if V7_ENABLE__Date__UTC -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Date_UTC(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - etime_t ret_time; - - if (!d_iscalledasfunction(v7, this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Date.now() called on object"); - goto clean; - } - - ret_time = d_time_number_from_arr(v7, tpyear, 0, d_gmktime); - *res = v7_mk_number(v7, ret_time); - -clean: - return rcode; -} -#endif /* V7_ENABLE__Date__UTC */ - -/****** Initialization *******/ - -/* - * We should clear V7_PROPERTY_ENUMERABLE for all Date props - * TODO(mkm): check other objects -*/ -static int d_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)); -} - -#define DECLARE_GET(func) \ - d_set_cfunc_prop(v7, v7->vals.date_prototype, "getUTC" #func, \ - Date_getUTC##func); \ - d_set_cfunc_prop(v7, v7->vals.date_prototype, "get" #func, Date_get##func); - -#define DECLARE_SET(func) \ - d_set_cfunc_prop(v7, v7->vals.date_prototype, "setUTC" #func, \ - Date_setUTC##func); \ - d_set_cfunc_prop(v7, v7->vals.date_prototype, "set" #func, Date_set##func); - -V7_PRIVATE void init_date(struct v7 *v7) { - val_t date = - mk_cfunction_obj_with_proto(v7, Date_ctor, 7, v7->vals.date_prototype); - v7_def(v7, v7->vals.global_object, "Date", 4, V7_DESC_ENUMERABLE(0), date); - d_set_cfunc_prop(v7, v7->vals.date_prototype, "valueOf", Date_valueOf); - -#if V7_ENABLE__Date__getters - DECLARE_GET(Date); - DECLARE_GET(FullYear); - DECLARE_GET(Month); - DECLARE_GET(Hours); - DECLARE_GET(Minutes); - DECLARE_GET(Seconds); - DECLARE_GET(Milliseconds); - DECLARE_GET(Day); - d_set_cfunc_prop(v7, v7->vals.date_prototype, "getTime", Date_getTime); -#endif - -#if V7_ENABLE__Date__setters - DECLARE_SET(Date); - DECLARE_SET(FullYear); - DECLARE_SET(Month); - DECLARE_SET(Hours); - DECLARE_SET(Minutes); - DECLARE_SET(Seconds); - DECLARE_SET(Milliseconds); - d_set_cfunc_prop(v7, v7->vals.date_prototype, "setTime", Date_setTime); - d_set_cfunc_prop(v7, v7->vals.date_prototype, "getTimezoneOffset", - Date_getTimezoneOffset); -#endif - -#if V7_ENABLE__Date__now - d_set_cfunc_prop(v7, date, "now", Date_now); -#endif -#if V7_ENABLE__Date__parse - d_set_cfunc_prop(v7, date, "parse", Date_parse); -#endif -#if V7_ENABLE__Date__UTC - d_set_cfunc_prop(v7, date, "UTC", Date_UTC); -#endif - -#if V7_ENABLE__Date__toString - d_set_cfunc_prop(v7, v7->vals.date_prototype, "toString", Date_toString); - d_set_cfunc_prop(v7, v7->vals.date_prototype, "toISOString", - Date_toISOString); - d_set_cfunc_prop(v7, v7->vals.date_prototype, "toUTCString", - Date_toUTCString); - d_set_cfunc_prop(v7, v7->vals.date_prototype, "toDateString", - Date_toDateString); - d_set_cfunc_prop(v7, v7->vals.date_prototype, "toTimeString", - Date_toTimeString); -#endif -#if V7_ENABLE__Date__toLocaleString - d_set_cfunc_prop(v7, v7->vals.date_prototype, "toLocaleString", - Date_toLocaleString); - d_set_cfunc_prop(v7, v7->vals.date_prototype, "toLocaleDateString", - Date_toLocaleDateString); - d_set_cfunc_prop(v7, v7->vals.date_prototype, "toLocaleTimeString", - Date_toLocaleTimeString); -#endif -#if V7_ENABLE__Date__toJSON - d_set_cfunc_prop(v7, v7->vals.date_prototype, "toJSON", Date_toJSON); -#endif - - /* - * GTM offset without DST - * TODO(alashkin): check this - * Could be changed to tm::tm_gmtoff, - * but tm_gmtoff includes DST, so - * side effects are possible - */ - tzset(); - g_gmtoffms = timezone * msPerSecond; - /* - * tzname could be changed by localtime_r call, - * so we have to store pointer - * TODO(alashkin): need restart on tz change??? - */ - g_tzname = tzname[0]; -} - -#if defined(__cplusplus) -} -#endif /* __cplusplus */ - -#endif /* V7_ENABLE__Date */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_function.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/core.h" */ -/* Amalgamated: #include "v7/src/function.h" */ -/* Amalgamated: #include "v7/src/bcode.h" */ -/* Amalgamated: #include "v7/src/eval.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "v7/src/object.h" */ -/* Amalgamated: #include "v7/src/exec.h" */ -/* Amalgamated: #include "v7/src/exceptions.h" */ - -#if defined(__cplusplus) -extern "C" { -#endif /* __cplusplus */ - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Function_ctor(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - long i, num_args = v7_argc(v7); - size_t size; - const char *s; - struct mbuf m; - - mbuf_init(&m, 0); - - if (num_args <= 0) { - goto clean; - } - - mbuf_append(&m, "(function(", 10); - - for (i = 0; i < num_args - 1; i++) { - rcode = obj_value_of(v7, v7_arg(v7, i), res); - if (rcode != V7_OK) { - goto clean; - } - if (v7_is_string(*res)) { - if (i > 0) mbuf_append(&m, ",", 1); - s = v7_get_string(v7, res, &size); - mbuf_append(&m, s, size); - } - } - mbuf_append(&m, "){", 2); - rcode = obj_value_of(v7, v7_arg(v7, num_args - 1), res); - if (rcode != V7_OK) { - goto clean; - } - if (v7_is_string(*res)) { - s = v7_get_string(v7, res, &size); - mbuf_append(&m, s, size); - } - mbuf_append(&m, "})\0", 3); - - rcode = v7_exec(v7, m.buf, res); - if (rcode != V7_OK) { - rcode = v7_throwf(v7, SYNTAX_ERROR, "Invalid function body"); - goto clean; - } - -clean: - mbuf_free(&m); - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Function_length(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - v7_val_t this_obj = v7_get_this(v7); - struct v7_js_function *func; - - rcode = obj_value_of(v7, this_obj, &this_obj); - if (rcode != V7_OK) { - goto clean; - } - if (!is_js_function(this_obj)) { - *res = v7_mk_number(v7, 0); - goto clean; - } - - func = get_js_function_struct(this_obj); - - *res = v7_mk_number(v7, func->bcode->args_cnt); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Function_name(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - v7_val_t this_obj = v7_get_this(v7); - struct v7_js_function *func; - - rcode = obj_value_of(v7, this_obj, &this_obj); - if (rcode != V7_OK) { - goto clean; - } - if (!is_js_function(this_obj)) { - goto clean; - } - - func = get_js_function_struct(this_obj); - - assert(func->bcode != NULL); - - assert(func->bcode->names_cnt >= 1); - bcode_next_name_v(v7, func->bcode, func->bcode->ops.p, res); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Function_apply(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t this_arg = v7_arg(v7, 0); - val_t func_args = v7_arg(v7, 1); - - rcode = obj_value_of(v7, this_obj, &this_obj); - if (rcode != V7_OK) { - goto clean; - } - - if (is_js_function(this_obj)) { - /* - * `Function_apply` is a cfunction, so, GC is inhibited before calling it. - * But the given function to call is a JS function, so we should enable GC; - * otherwise, it will be inhibited during the whole execution of the given - * JS function - */ - v7_set_gc_enabled(v7, 1); - } - - rcode = b_apply(v7, this_obj, this_arg, func_args, 0, res); - if (rcode != V7_OK) { - goto clean; - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Function_toString(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - char *ops; - char *name; - size_t name_len; - char buf[50]; - char *b = buf; - struct v7_js_function *func = get_js_function_struct(v7_get_this(v7)); - int i; - - b += c_snprintf(b, BUF_LEFT(sizeof(buf), b - buf), "[function"); - - assert(func->bcode != NULL); - ops = func->bcode->ops.p; - - /* first entry in name list */ - ops = bcode_next_name(ops, &name, &name_len); - - if (name_len > 0) { - b += c_snprintf(b, BUF_LEFT(sizeof(buf), b - buf), " %.*s", (int) name_len, - name); - } - b += c_snprintf(b, BUF_LEFT(sizeof(buf), b - buf), "("); - for (i = 0; i < func->bcode->args_cnt; i++) { - ops = bcode_next_name(ops, &name, &name_len); - - b += c_snprintf(b, BUF_LEFT(sizeof(buf), b - buf), "%.*s", (int) name_len, - name); - if (i < func->bcode->args_cnt - 1) { - b += c_snprintf(b, BUF_LEFT(sizeof(buf), b - buf), ","); - } - } - b += c_snprintf(b, BUF_LEFT(sizeof(buf), b - buf), ")"); - - { - uint8_t loc_cnt = - func->bcode->names_cnt - func->bcode->args_cnt - 1 /*func name*/; - - if (loc_cnt > 0) { - b += c_snprintf(b, BUF_LEFT(sizeof(buf), b - buf), "{var "); - for (i = 0; i < loc_cnt; ++i) { - ops = bcode_next_name(ops, &name, &name_len); - - b += c_snprintf(b, BUF_LEFT(sizeof(buf), b - buf), "%.*s", - (int) name_len, name); - if (i < (loc_cnt - 1)) { - b += c_snprintf(b, BUF_LEFT(sizeof(buf), b - buf), ","); - } - } - - b += c_snprintf(b, BUF_LEFT(sizeof(buf), b - buf), "}"); - } - } - - b += c_snprintf(b, BUF_LEFT(sizeof(buf), b - buf), "]"); - - *res = v7_mk_string(v7, buf, strlen(buf), 1); - - return rcode; -} - -V7_PRIVATE void init_function(struct v7 *v7) { - val_t ctor = mk_cfunction_obj(v7, Function_ctor, 1); - - v7_set(v7, ctor, "prototype", 9, v7->vals.function_prototype); - v7_set(v7, v7->vals.global_object, "Function", 8, ctor); - set_method(v7, v7->vals.function_prototype, "apply", Function_apply, 1); - set_method(v7, v7->vals.function_prototype, "toString", Function_toString, 0); - v7_def(v7, v7->vals.function_prototype, "length", 6, - (V7_DESC_ENUMERABLE(0) | V7_DESC_GETTER(1)), - v7_mk_cfunction(Function_length)); - v7_def(v7, v7->vals.function_prototype, "name", 4, - (V7_DESC_ENUMERABLE(0) | V7_DESC_GETTER(1)), - v7_mk_cfunction(Function_name)); -} - -#if defined(__cplusplus) -} -#endif /* __cplusplus */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_regex.c" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* Amalgamated: #include "common/utf.h" */ -/* Amalgamated: #include "common/str_util.h" */ -/* Amalgamated: #include "v7/src/internal.h" */ -/* Amalgamated: #include "v7/src/std_regex.h" */ -/* Amalgamated: #include "v7/src/std_string.h" */ -/* Amalgamated: #include "v7/src/slre.h" */ -/* Amalgamated: #include "v7/src/core.h" */ -/* Amalgamated: #include "v7/src/function.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "v7/src/array.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/primitive.h" */ - -#if V7_ENABLE__RegExp - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Regex_ctor(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - long argnum = v7_argc(v7); - - if (argnum > 0) { - val_t arg = v7_arg(v7, 0); - val_t ro, fl; - size_t re_len, flags_len = 0; - const char *re, *flags = NULL; - - if (v7_is_regexp(v7, arg)) { - if (argnum > 1) { - /* ch15/15.10/15.10.3/S15.10.3.1_A2_T1.js */ - rcode = v7_throwf(v7, TYPE_ERROR, "invalid flags"); - goto clean; - } - *res = arg; - goto clean; - } - rcode = to_string(v7, arg, &ro, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - if (argnum > 1) { - rcode = to_string(v7, v7_arg(v7, 1), &fl, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - - flags = v7_get_string(v7, &fl, &flags_len); - } - re = v7_get_string(v7, &ro, &re_len); - rcode = v7_mk_regexp(v7, re, re_len, flags, flags_len, res); - if (rcode != V7_OK) { - goto clean; - } - - } else { - rcode = v7_mk_regexp(v7, "(?:)", 4, NULL, 0, res); - if (rcode != V7_OK) { - goto clean; - } - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Regex_global(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - int flags = 0; - val_t this_obj = v7_get_this(v7); - val_t r = V7_UNDEFINED; - rcode = obj_value_of(v7, this_obj, &r); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_is_regexp(v7, r)) { - flags = slre_get_flags(v7_get_regexp_struct(v7, r)->compiled_regexp); - } - - *res = v7_mk_boolean(v7, flags & SLRE_FLAG_G); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Regex_ignoreCase(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - int flags = 0; - val_t this_obj = v7_get_this(v7); - val_t r = V7_UNDEFINED; - rcode = obj_value_of(v7, this_obj, &r); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_is_regexp(v7, r)) { - flags = slre_get_flags(v7_get_regexp_struct(v7, r)->compiled_regexp); - } - - *res = v7_mk_boolean(v7, flags & SLRE_FLAG_I); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Regex_multiline(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - int flags = 0; - val_t this_obj = v7_get_this(v7); - val_t r = V7_UNDEFINED; - rcode = obj_value_of(v7, this_obj, &r); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_is_regexp(v7, r)) { - flags = slre_get_flags(v7_get_regexp_struct(v7, r)->compiled_regexp); - } - - *res = v7_mk_boolean(v7, flags & SLRE_FLAG_M); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Regex_source(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t r = V7_UNDEFINED; - const char *buf = 0; - size_t len = 0; - - rcode = obj_value_of(v7, this_obj, &r); - if (rcode != V7_OK) { - goto clean; - } - - if (v7_is_regexp(v7, r)) { - buf = v7_get_string(v7, &v7_get_regexp_struct(v7, r)->regexp_string, &len); - } - - *res = v7_mk_string(v7, buf, len, 1); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Regex_get_lastIndex(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - long lastIndex = 0; - val_t this_obj = v7_get_this(v7); - - if (v7_is_regexp(v7, this_obj)) { - lastIndex = v7_get_regexp_struct(v7, this_obj)->lastIndex; - } - - *res = v7_mk_number(v7, lastIndex); - - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Regex_set_lastIndex(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - long lastIndex = 0; - val_t this_obj = v7_get_this(v7); - - if (v7_is_regexp(v7, this_obj)) { - rcode = to_long(v7, v7_arg(v7, 0), 0, &lastIndex); - if (rcode != V7_OK) { - goto clean; - } - v7_get_regexp_struct(v7, this_obj)->lastIndex = lastIndex; - } - - *res = v7_mk_number(v7, lastIndex); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err rx_exec(struct v7 *v7, val_t rx, val_t vstr, int lind, - val_t *res) { - enum v7_err rcode = V7_OK; - if (v7_is_regexp(v7, rx)) { - val_t s = V7_UNDEFINED; - size_t len; - struct slre_loot sub; - struct slre_cap *ptok = sub.caps; - const char *str = NULL; - const char *end = NULL; - const char *begin = NULL; - struct v7_regexp *rp = v7_get_regexp_struct(v7, rx); - int flag_g = slre_get_flags(rp->compiled_regexp) & SLRE_FLAG_G; - - rcode = to_string(v7, vstr, &s, NULL, 0, NULL); - if (rcode != V7_OK) { - goto clean; - } - str = v7_get_string(v7, &s, &len); - end = str + len; - begin = str; - - if (rp->lastIndex < 0) rp->lastIndex = 0; - if (flag_g || lind) begin = utfnshift(str, rp->lastIndex); - - if (!slre_exec(rp->compiled_regexp, 0, begin, end, &sub)) { - int i; - val_t arr = v7_mk_array(v7); - char *old_mbuf_base = v7->owned_strings.buf; - ptrdiff_t rel = 0; /* creating strings might relocate the mbuf */ - - for (i = 0; i < sub.num_captures; i++, ptok++) { - rel = v7->owned_strings.buf - old_mbuf_base; - v7_array_push(v7, arr, v7_mk_string(v7, ptok->start + rel, - ptok->end - ptok->start, 1)); - } - if (flag_g) rp->lastIndex = utfnlen(str, sub.caps->end + rel - str); - v7_def(v7, arr, "index", 5, V7_DESC_WRITABLE(0), - v7_mk_number(v7, utfnlen(str + rel, sub.caps->start - str))); - *res = arr; - goto clean; - } else { - rp->lastIndex = 0; - } - } - - *res = V7_NULL; - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Regex_exec(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - - if (v7_argc(v7) > 0) { - rcode = rx_exec(v7, this_obj, v7_arg(v7, 0), 0, res); - if (rcode != V7_OK) { - goto clean; - } - } else { - *res = V7_NULL; - } - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Regex_test(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t tmp = V7_UNDEFINED; - - rcode = Regex_exec(v7, &tmp); - if (rcode != V7_OK) { - goto clean; - } - - *res = v7_mk_boolean(v7, !v7_is_null(tmp)); - -clean: - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Regex_flags(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - char buf[3] = {0}; - val_t this_obj = v7_get_this(v7); - struct v7_regexp *rp = v7_get_regexp_struct(v7, this_obj); - size_t n = get_regexp_flags_str(v7, rp, buf); - *res = v7_mk_string(v7, buf, n, 1); - - return rcode; -} - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Regex_toString(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - size_t n1, n2 = 0; - char s2[3] = {0}; - char buf[50]; - val_t this_obj = v7_get_this(v7); - struct v7_regexp *rp; - const char *s1; - - rcode = obj_value_of(v7, this_obj, &this_obj); - if (rcode != V7_OK) { - goto clean; - } - - if (!v7_is_regexp(v7, this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Not a regexp"); - goto clean; - } - - rp = v7_get_regexp_struct(v7, this_obj); - s1 = v7_get_string(v7, &rp->regexp_string, &n1); - n2 = get_regexp_flags_str(v7, rp, s2); - - c_snprintf(buf, sizeof(buf), "/%.*s/%.*s", (int) n1, s1, (int) n2, s2); - - *res = v7_mk_string(v7, buf, strlen(buf), 1); - -clean: - return rcode; -} - -V7_PRIVATE void init_regex(struct v7 *v7) { - val_t ctor = - mk_cfunction_obj_with_proto(v7, Regex_ctor, 1, v7->vals.regexp_prototype); - val_t lastIndex = v7_mk_dense_array(v7); - - v7_def(v7, v7->vals.global_object, "RegExp", 6, V7_DESC_ENUMERABLE(0), ctor); - - set_cfunc_prop(v7, v7->vals.regexp_prototype, "exec", Regex_exec); - set_cfunc_prop(v7, v7->vals.regexp_prototype, "test", Regex_test); - set_method(v7, v7->vals.regexp_prototype, "toString", Regex_toString, 0); - - v7_def(v7, v7->vals.regexp_prototype, "global", 6, V7_DESC_GETTER(1), - v7_mk_cfunction(Regex_global)); - v7_def(v7, v7->vals.regexp_prototype, "ignoreCase", 10, V7_DESC_GETTER(1), - v7_mk_cfunction(Regex_ignoreCase)); - v7_def(v7, v7->vals.regexp_prototype, "multiline", 9, V7_DESC_GETTER(1), - v7_mk_cfunction(Regex_multiline)); - v7_def(v7, v7->vals.regexp_prototype, "source", 6, V7_DESC_GETTER(1), - v7_mk_cfunction(Regex_source)); - v7_def(v7, v7->vals.regexp_prototype, "flags", 5, V7_DESC_GETTER(1), - v7_mk_cfunction(Regex_flags)); - - v7_array_set(v7, lastIndex, 0, v7_mk_cfunction(Regex_get_lastIndex)); - v7_array_set(v7, lastIndex, 1, v7_mk_cfunction(Regex_set_lastIndex)); - v7_def(v7, v7->vals.regexp_prototype, "lastIndex", 9, - (V7_DESC_GETTER(1) | V7_DESC_SETTER(1)), lastIndex); -} - -#endif /* V7_ENABLE__RegExp */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/std_proxy.c" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* Amalgamated: #include "v7/src/internal.h" */ -/* Amalgamated: #include "v7/src/std_object.h" */ -/* Amalgamated: #include "v7/src/std_proxy.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "v7/src/core.h" */ -/* Amalgamated: #include "v7/src/function.h" */ -/* Amalgamated: #include "v7/src/object.h" */ -/* Amalgamated: #include "v7/src/primitive.h" */ -/* Amalgamated: #include "v7/src/string.h" */ -/* Amalgamated: #include "v7/src/exceptions.h" */ - -#if defined(__cplusplus) -extern "C" { -#endif /* __cplusplus */ - -#if V7_ENABLE__Proxy - -WARN_UNUSED_RESULT -V7_PRIVATE enum v7_err Proxy_ctor(struct v7 *v7, v7_val_t *res) { - enum v7_err rcode = V7_OK; - val_t this_obj = v7_get_this(v7); - val_t target_v = v7_arg(v7, 0); - val_t handler_v = v7_arg(v7, 1); - struct v7_object *t = NULL; - v7_prop_attr_desc_t attrs_desc = - (V7_DESC_WRITABLE(0) | V7_DESC_ENUMERABLE(0) | V7_DESC_CONFIGURABLE(0)); - - if (this_obj == v7_get_global(v7) || !v7_is_object(this_obj)) { - rcode = v7_throwf(v7, TYPE_ERROR, "Wrong 'this' object for Proxy ctor"); - goto clean; - } - - if (!v7_is_object(target_v) || !v7_is_object(handler_v)) { - rcode = - v7_throwf(v7, TYPE_ERROR, - "Cannot create proxy with a non-object as target or handler"); - goto clean; - } - - t = get_object_struct(this_obj); - t->attributes |= V7_OBJ_PROXY; - - v7_def(v7, this_obj, _V7_PROXY_TARGET_NAME, ~0, attrs_desc, target_v); - v7_def(v7, this_obj, _V7_PROXY_HANDLER_NAME, ~0, attrs_desc, handler_v); - - (void) res; - -clean: - return rcode; -} - -V7_PRIVATE void init_proxy(struct v7 *v7) { - /*v7_prop_attr_desc_t attrs_desc =*/ - /*(V7_DESC_WRITABLE(0) | V7_DESC_ENUMERABLE(0) | V7_DESC_CONFIGURABLE(0));*/ - val_t proxy = - mk_cfunction_obj_with_proto(v7, Proxy_ctor, 1, v7->vals.proxy_prototype); - - v7_def(v7, v7->vals.global_object, "Proxy", ~0, V7_DESC_ENUMERABLE(0), proxy); -} - -V7_PRIVATE int is_special_proxy_name(const char *name, size_t name_len) { - int ret = 0; - if (name_len == (size_t) ~0) { - name_len = strlen(name); - } - if (name_len == 5 && (memcmp(name, _V7_PROXY_TARGET_NAME, name_len) == 0 || - memcmp(name, _V7_PROXY_HANDLER_NAME, name_len) == 0)) { - ret = 1; - } - return ret; -} - -#endif /* V7_ENABLE__Proxy */ - -#if defined(__cplusplus) -} -#endif /* __cplusplus */ -#ifdef V7_MODULE_LINES -#line 1 "v7/src/main.c" -#endif -/* - * Copyright (c) 2014 Cesanta Software Limited - * All rights reserved - */ - -/* Amalgamated: #include "v7/src/internal.h" */ -/* Amalgamated: #include "v7/src/gc.h" */ -/* Amalgamated: #include "v7/src/freeze.h" */ -/* Amalgamated: #include "v7/src/main.h" */ -/* Amalgamated: #include "v7/src/primitive.h" */ -/* Amalgamated: #include "v7/src/exec.h" */ -/* Amalgamated: #include "v7/src/util.h" */ -/* Amalgamated: #include "v7/src/conversion.h" */ -/* Amalgamated: #include "common/platform.h" */ -/* Amalgamated: #include "common/cs_file.h" */ - -#if defined(_MSC_VER) && _MSC_VER >= 1800 -#define fileno _fileno -#endif - -#ifdef V7_EXE -#define V7_MAIN -#endif - -#ifdef V7_MAIN - -#include <sys/stat.h> - -static void show_usage(char *argv[]) { - fprintf(stderr, "V7 version %s (c) Cesanta Software, built on %s\n", - V7_VERSION, __DATE__); - fprintf(stderr, "Usage: %s [OPTIONS] js_file ...\n", argv[0]); - fprintf(stderr, "%s\n", "OPTIONS:"); - fprintf(stderr, "%s\n", " -e <expr> execute expression"); - fprintf(stderr, "%s\n", " -t dump generated text AST"); - fprintf(stderr, "%s\n", " -b dump generated binary AST"); - fprintf(stderr, "%s\n", " -c dump compiled binary bcode"); - fprintf(stderr, "%s\n", " -mm dump memory stats"); - fprintf(stderr, "%s\n", " -vo <n> object arena size"); - fprintf(stderr, "%s\n", " -vf <n> function arena size"); - fprintf(stderr, "%s\n", " -vp <n> property arena size"); -#ifdef V7_FREEZE - fprintf(stderr, "%s\n", " -freeze filename dump JS heap into a file"); -#endif - exit(EXIT_FAILURE); -} - -#if V7_ENABLE__Memory__stats -static void dump_mm_arena_stats(const char *msg, struct gc_arena *a) { - printf("%s: total allocations %lu, total garbage %lu, max %" SIZE_T_FMT - ", alive %lu\n", - msg, a->allocations, a->garbage, gc_arena_size(a), a->alive); - printf( - "%s: (bytes: total allocations %lu, total garbage %lu, max %" SIZE_T_FMT - ", alive %lu)\n", - msg, a->allocations * a->cell_size, a->garbage * a->cell_size, - gc_arena_size(a) * a->cell_size, a->alive * a->cell_size); -} - -static void dump_mm_stats(struct v7 *v7) { - dump_mm_arena_stats("object: ", &v7->generic_object_arena); - dump_mm_arena_stats("function: ", &v7->function_arena); - dump_mm_arena_stats("property: ", &v7->property_arena); - printf("string arena len: %" SIZE_T_FMT "\n", v7->owned_strings.len); - printf("Total heap size: %" SIZE_T_FMT "\n", - v7->owned_strings.len + - 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); -} -#endif - -int v7_main(int argc, char *argv[], void (*pre_freeze_init)(struct v7 *), - void (*pre_init)(struct v7 *), void (*post_init)(struct v7 *)) { - int exit_rcode = EXIT_SUCCESS; - struct v7 *v7; - struct v7_create_opts opts; - int as_json = 0; - int i, j, show_ast = 0, binary_ast = 0, dump_bcode = 0, dump_stats = 0; - val_t res; - int nexprs = 0; - const char *exprs[16]; - - memset(&opts, 0, sizeof(opts)); - - (void) show_ast; - (void) binary_ast; - (void) dump_bcode; - - /* Execute inline code */ - for (i = 1; i < argc && argv[i][0] == '-'; i++) { - if (strcmp(argv[i], "-e") == 0 && i + 1 < argc) { - exprs[nexprs++] = argv[i + 1]; - i++; - } else if (strcmp(argv[i], "-t") == 0) { - show_ast = 1; - } else if (strcmp(argv[i], "-b") == 0) { - show_ast = 1; - binary_ast = 1; - } else if (strcmp(argv[i], "-c") == 0) { - binary_ast = 1; - dump_bcode = 1; - } else if (strcmp(argv[i], "-h") == 0) { - show_usage(argv); - } else if (strcmp(argv[i], "-j") == 0) { - as_json = 1; -#if V7_ENABLE__Memory__stats - } else if (strcmp(argv[i], "-mm") == 0) { - dump_stats = 1; -#endif - } else if (strcmp(argv[i], "-vo") == 0 && i + 1 < argc) { - opts.object_arena_size = atoi(argv[i + 1]); - i++; - } else if (strcmp(argv[i], "-vf") == 0 && i + 1 < argc) { - opts.function_arena_size = atoi(argv[i + 1]); - i++; - } else if (strcmp(argv[i], "-vp") == 0 && i + 1 < argc) { - opts.property_arena_size = atoi(argv[i + 1]); - i++; - } -#ifdef V7_FREEZE - else if (strcmp(argv[i], "-freeze") == 0 && i + 1 < argc) { - opts.freeze_file = argv[i + 1]; - i++; - } -#endif - } - -#ifndef V7_ALLOW_ARGLESS_MAIN - if (argc == 1) { - show_usage(argv); - } -#endif - - v7 = v7_create_opt(opts); - res = V7_UNDEFINED; - - if (pre_freeze_init != NULL) { - pre_freeze_init(v7); - } - -#ifdef V7_FREEZE - /* - * Skip pre_init if freezing, but still execute cmdline expressions. - * This makes it easier to add custom code when freezing from cmdline. - */ - if (opts.freeze_file == NULL) { -#endif - - if (pre_init != NULL) { - pre_init(v7); - } - -#ifdef V7_FREEZE - } -#endif - -#if V7_ENABLE__Memory__stats > 0 && !defined(V7_DISABLE_GC) - if (dump_stats) { - printf("Memory stats during init:\n"); - dump_mm_stats(v7); - v7_gc(v7, 0); - printf("Memory stats before run:\n"); - dump_mm_stats(v7); - } -#else - (void) dump_stats; -#endif - - /* Execute inline expressions */ - for (j = 0; j < nexprs; j++) { - enum v7_err (*exec)(struct v7 *, const char *, v7_val_t *); - exec = v7_exec; - - if (show_ast || dump_bcode) { -#if !defined(V7_NO_COMPILER) - if (v7_compile(exprs[j], binary_ast, dump_bcode, stdout) != V7_OK) { - exit_rcode = EXIT_FAILURE; - fprintf(stderr, "%s\n", "parse error"); - } -#else /* V7_NO_COMPILER */ - exit_rcode = EXIT_FAILURE; - fprintf(stderr, "%s\n", "Parsing is disabled by V7_NO_COMPILER"); -#endif /* V7_NO_COMPILER */ - } else if (exec(v7, exprs[j], &res) != V7_OK) { - v7_print_error(stderr, v7, exprs[j], res); - exit_rcode = EXIT_FAILURE; - res = V7_UNDEFINED; - } - } - - /* Execute files */ - for (; i < argc; i++) { - if (show_ast || dump_bcode) { -#if !defined(V7_NO_COMPILER) - size_t size; - char *source_code; - if ((source_code = cs_read_file(argv[i], &size)) == NULL) { - exit_rcode = EXIT_FAILURE; - fprintf(stderr, "Cannot read [%s]\n", argv[i]); - } else { - if (_v7_compile(source_code, size, binary_ast, dump_bcode, stdout) != - V7_OK) { - fprintf(stderr, "error: %s\n", v7->error_msg); - exit_rcode = EXIT_FAILURE; - exit(exit_rcode); - } - free(source_code); - } -#else /* V7_NO_COMPILER */ - exit_rcode = EXIT_FAILURE; - fprintf(stderr, "%s\n", "Parsing is disabled by V7_NO_COMPILER"); -#endif /* V7_NO_COMPILER */ - } else if (v7_exec_file(v7, argv[i], &res) != V7_OK) { - v7_print_error(stderr, v7, argv[i], res); - res = V7_UNDEFINED; - } - } - -#ifdef V7_FREEZE - if (opts.freeze_file != NULL) { - freeze(v7, opts.freeze_file); - exit(0); - } -#endif - - if (!(show_ast || dump_bcode)) { - char buf[2000]; - char *s = v7_stringify(v7, res, buf, sizeof(buf), - as_json ? V7_STRINGIFY_JSON : V7_STRINGIFY_DEBUG); - printf("%s\n", s); - if (s != buf) { - free(s); - } - } - - if (post_init != NULL) { - post_init(v7); - } - -#if V7_ENABLE__Memory__stats - if (dump_stats) { - printf("Memory stats after run:\n"); - dump_mm_stats(v7); - } -#else - (void) dump_stats; -#endif - - v7_destroy(v7); - return exit_rcode; -} -#endif - -#ifdef V7_EXE -int main(int argc, char *argv[]) { - return v7_main(argc, argv, NULL, NULL, NULL); -} -#endif -#endif /* V7_EXPORT_INTERNAL_HEADERS */
--- a/ICE-Application/src/add-ons/v7/v7.h Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1752 +0,0 @@ -#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 <http://www.gnu.org/licenses/>. - * - * 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 <https://www.cesanta.com/license>. - */ - -#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 "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 -#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/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" */ - -#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/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 <stddef.h> /* For size_t */ -#include <stdio.h> /* 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 <inttypes.h> -#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 defined(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_ */ -#ifndef V7_EXPORT_INTERNAL_HEADERS -#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 <stddef.h> /* For size_t */ -#include <stdio.h> /* 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 <inttypes.h> -#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 defined(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/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/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/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/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/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/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/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/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. - * - * 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); - -/* - * 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/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/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/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/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_ */ -#endif /* V7_EXPORT_INTERNAL_HEADERS */