mirror of
https://github.com/espruino/Espruino.git
synced 2026-02-01 15:55:37 +00:00
esp8266: move lots of string constants to flash
This commit is contained in:
parent
a2d4048fe6
commit
1be478dd32
28
Makefile
28
Makefile
@ -528,7 +528,12 @@ USE_NET=1
|
||||
BOARD=ESP8266_BOARD
|
||||
# Enable link-time optimisations (inlining across files) but don't go beyond -O2 'cause of
|
||||
# code size explosion, also -DLINK_TIME_OPTIMISATION leads to too big a firmware
|
||||
ifndef DISABLE_LTO
|
||||
OPTIMIZEFLAGS+=-Os -std=gnu11 -fgnu89-inline -flto -fno-fat-lto-objects -Wl,--allow-multiple-definition
|
||||
else
|
||||
# DISABLE_LTO is necessary in order to analyze static string sizes (topstring makefile target)
|
||||
OPTIMIZEFLAGS+=-Os -std=gnu11 -fgnu89-inline -Wl,--allow-multiple-definition
|
||||
endif
|
||||
ESP_FLASH_MAX ?= 491520 # max bin file: 480KB
|
||||
|
||||
ifdef FLASH_4MB
|
||||
@ -1649,6 +1654,22 @@ $(USER1_BIN): $(USER1_ELF)
|
||||
@echo "** user1.bin uses $$(stat -c '%s' $@) bytes of" $(ESP_FLASH_MAX) "available"
|
||||
@if [ $$(stat -c '%s' $@) -gt $$(( $(ESP_FLASH_MAX) )) ]; then echo "$@ too big!"; false; fi
|
||||
|
||||
# Analyze all the .o files and rank them by the amount of static string area used, useful to figure
|
||||
# out where to optimize and move strings to flash
|
||||
# IMPORTANT: this only works if DISABLE_LTO id defined, e.g. `DISABLE_LTO=1 make`
|
||||
topstrings: $(PARTIAL)
|
||||
$(Q)for f in `find . -name \*.o`; do \
|
||||
str=$$($(OBJDUMP) -j .rodata.str1.4 -h $$f 2>/dev/null | \
|
||||
egrep -o 'rodata.str1.4 [0-9a-f]+' | \
|
||||
awk $$(expr match "$$(awk --version)" "GNU.*" >/dev/null && echo --non-decimal-data) \
|
||||
-e '{printf "%d\n", ("0x" $$2);}'); \
|
||||
[ "$$str" ] && echo "$$str $$f"; \
|
||||
done | \
|
||||
sort -rn >topstrings
|
||||
$(Q)echo "Top 20 from ./topstrings:"
|
||||
$(Q)head -20 topstrings
|
||||
$(Q)echo "To get details: $(OBJDUMP) -j .rodata.str1.4 -s src/FILENAME.o"
|
||||
|
||||
# generate binary image for user2, i.e. second OTA partition
|
||||
# we make this rule dependent on user1.bin in order to serialize the two rules because they use
|
||||
# stupid static filenames (go blame the Espressif tool)
|
||||
@ -1676,6 +1697,13 @@ ifndef COMPORT
|
||||
endif
|
||||
-$(ESPTOOL) --port $(COMPORT) --baud 460800 write_flash --flash_freq $(ET_FF) --flash_mode qio --flash_size $(ET_FS) 0x0000 "$(BOOTLOADER)" 0x1000 $(USER1_BIN) $(ET_BLANK) $(BLANK)
|
||||
|
||||
# just flash user1 and don't mess with bootloader or wifi settings
|
||||
quickflash: all $(USER1_BIN) $(USER2_BIN)
|
||||
ifndef COMPORT
|
||||
$(error "In order to flash, we need to have the COMPORT variable defined")
|
||||
endif
|
||||
-$(ESPTOOL) --port $(COMPORT) --baud 460800 write_flash 0x1000 $(USER1_BIN)
|
||||
|
||||
wiflash: all $(USER1_BIN) $(USER2_BIN)
|
||||
ifndef ESPHOSTNAME
|
||||
$(error "In order to flash over wifi, we need to have the ESPHOSTNAME variable defined")
|
||||
|
||||
@ -84,40 +84,48 @@ static struct ping_option pingOpt;
|
||||
static uint8 g_preWiFiScanMode;
|
||||
|
||||
// Reasons for which a connection failed
|
||||
FLASH_STR(__wr0, "0 - <Not Used>"); // 0
|
||||
FLASH_STR(__wr1, "unspecified"); // 1 - REASON_UNSPECIFIED
|
||||
FLASH_STR(__wr2, "auth_expire"); // 2 - REASON_AUTH_EXPIRE
|
||||
FLASH_STR(__wr3, "auth_leave"); // 3 - REASON_AUTH_LEAVE
|
||||
FLASH_STR(__wr4, "assoc_expire"); // 4 - REASON_ASSOC_EXPIRE
|
||||
FLASH_STR(__wr5, "assoc_toomany"); // 5 - REASON_ASSOC_TOOMANY
|
||||
FLASH_STR(__wr6, "not_authed"); // 6 - REASON_NOT_AUTHED
|
||||
FLASH_STR(__wr7, "not_assoced"); // 7 - REASON_NOT_ASSOCED
|
||||
FLASH_STR(__wr8, "assoc_leave"); // 8 - REASON_ASSOC_LEAVE
|
||||
FLASH_STR(__wr9, "assoc_not_authed"); // 9 - REASON_ASSOC_NOT_AUTHED
|
||||
FLASH_STR(__wr10, "disassoc_pwrcap_bad"); // 10 - REASON_DISASSOC_PWRCAP_BAD
|
||||
FLASH_STR(__wr11, "disassoc_supchan_bad"); // 11 - REASON_DISASSOC_SUPCHAN_BAD
|
||||
FLASH_STR(__wr12, "12 - <Not Used>"); // 12
|
||||
FLASH_STR(__wr13, "ie_invalid"); // 13 - REASON_IE_INVALID
|
||||
FLASH_STR(__wr14, "mic_failure"); // 14 - REASON_MIC_FAILURE
|
||||
FLASH_STR(__wr15, "4way_handshake_timeout"); // 15 - REASON_4WAY_HANDSHAKE_TIMEOUT
|
||||
FLASH_STR(__wr16, "group_key_update_timeout"); // 16 - REASON_GROUP_KEY_UPDATE_TIMEOUT
|
||||
FLASH_STR(__wr17, "ie_in_4way_differs"); // 17 - REASON_IE_IN_4WAY_DIFFERS
|
||||
FLASH_STR(__wr18, "group_cipher_invalid"); // 18 - REASON_GROUP_CIPHER_INVALID
|
||||
FLASH_STR(__wr19, "pairwise_cipher_invalid"); // 19 - REASON_PAIRWISE_CIPHER_INVALID
|
||||
FLASH_STR(__wr20, "akmp_invalid"); // 20 - REASON_AKMP_INVALID
|
||||
FLASH_STR(__wr21, "unsupp_rsn_ie_version"); // 21 - REASON_UNSUPP_RSN_IE_VERSION
|
||||
FLASH_STR(__wr22, "invalid_rsn_ie_cap"); // 22 - REASON_UNSUPP_RSN_IE_VERSION
|
||||
FLASH_STR(__wr23, "802_1x_auth_failed"); // 23 - REASON_802_1X_AUTH_FAILED
|
||||
FLASH_STR(__wr24, "cipher_suite_rejected"); // 24 - REASON_CIPHER_SUITE_REJECTED
|
||||
FLASH_STR(__wr200, "beacon_timeout"); // 200 - REASON_BEACON_TIMEOUT
|
||||
FLASH_STR(__wr201, "no_ap_found"); // 201 - REASON_NO_AP_FOUND
|
||||
static char *wifiReasons[] = {
|
||||
"0 - <Not Used>", // 0
|
||||
"unspecified", // 1 - REASON_UNSPECIFIED
|
||||
"auth_expire", // 2 - REASON_AUTH_EXPIRE
|
||||
"auth_leave", // 3 - REASON_AUTH_LEAVE
|
||||
"assoc_expire", // 4 - REASON_ASSOC_EXPIRE
|
||||
"assoc_toomany", // 5 - REASON_ASSOC_TOOMANY
|
||||
"not_authed", // 6 - REASON_NOT_AUTHED
|
||||
"not_assoced", // 7 - REASON_NOT_ASSOCED
|
||||
"assoc_leave", // 8 - REASON_ASSOC_LEAVE
|
||||
"assoc_not_authed", // 9 - REASON_ASSOC_NOT_AUTHED
|
||||
"disassoc_pwrcap_bad", // 10 - REASON_DISASSOC_PWRCAP_BAD
|
||||
"disassoc_supchan_bad", // 11 - REASON_DISASSOC_SUPCHAN_BAD
|
||||
"12 - <Not Used>", // 12
|
||||
"ie_invalid", // 13 - REASON_IE_INVALID
|
||||
"mic_failure", // 14 - REASON_MIC_FAILURE
|
||||
"4way_handshake_timeout", // 15 - REASON_4WAY_HANDSHAKE_TIMEOUT
|
||||
"group_key_update_timeout", // 16 - REASON_GROUP_KEY_UPDATE_TIMEOUT
|
||||
"ie_in_4way_differs", // 17 - REASON_IE_IN_4WAY_DIFFERS
|
||||
"group_cipher_invalid", // 18 - REASON_GROUP_CIPHER_INVALID
|
||||
"pairwise_cipher_invalid", // 19 - REASON_PAIRWISE_CIPHER_INVALID
|
||||
"akmp_invalid", // 20 - REASON_AKMP_INVALID
|
||||
"unsupp_rsn_ie_version", // 21 - REASON_UNSUPP_RSN_IE_VERSION
|
||||
"invalid_rsn_ie_cap", // 22 - REASON_UNSUPP_RSN_IE_VERSION
|
||||
"802_1x_auth_failed", // 23 - REASON_802_1X_AUTH_FAILED
|
||||
"cipher_suite_rejected", // 24 - REASON_CIPHER_SUITE_REJECTED
|
||||
"beacon_timeout", // 200 - REASON_BEACON_TIMEOUT
|
||||
"no_ap_found" // 201 - REASON_NO_AP_FOUND
|
||||
__wr0, __wr1, __wr2, __wr3, __wr4, __wr5, __wr6, __wr7, __wr8, __wr9, __wr10,
|
||||
__wr11, __wr12, __wr13, __wr14, __wr15, __wr16, __wr17, __wr18, __wr19, __wr20,
|
||||
__wr21, __wr22, __wr23, __wr24, __wr200, __wr201
|
||||
};
|
||||
|
||||
static char wifiReasonBuff[sizeof("group_key_update_timeout")+1]; // length of longest string
|
||||
static char *wifiGetReason(uint8 wifiReason) {
|
||||
if (wifiReason <= 24) return wifiReasons[wifiReason];
|
||||
if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+24];
|
||||
return wifiReasons[1];
|
||||
char *reason;
|
||||
if (wifiReason <= 24) reason = wifiReasons[wifiReason];
|
||||
else if (wifiReason >= 200 && wifiReason <= 201) reason = wifiReasons[wifiReason-200+24];
|
||||
else reason = wifiReasons[1];
|
||||
flash_strncpy(wifiReasonBuff, reason, sizeof(wifiReasonBuff));
|
||||
wifiReasonBuff[sizeof(wifiReasonBuff)-1] = 0; // force null termination
|
||||
return wifiReasonBuff;
|
||||
}
|
||||
|
||||
static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" };
|
||||
@ -2144,17 +2152,17 @@ static void ipAddrToString(struct ip_addr addr, char *string) {
|
||||
static char *wifiConnectStatusToString(uint8 status) {
|
||||
switch(status) {
|
||||
case STATION_IDLE:
|
||||
return "STATION_IDLE";
|
||||
return "IDLE";
|
||||
case STATION_CONNECTING:
|
||||
return "STATION_CONNECTING";
|
||||
return "CONNECTING";
|
||||
case STATION_WRONG_PASSWORD:
|
||||
return "STATION_WRONG_PASSWORD";
|
||||
return "WRONG_PASSWORD";
|
||||
case STATION_NO_AP_FOUND:
|
||||
return "STATION_NO_AP_FOUND";
|
||||
return "NO_AP_FOUND";
|
||||
case STATION_CONNECT_FAIL:
|
||||
return "STATION_CONNECT_FAIL";
|
||||
return "CONNECT_FAIL";
|
||||
case STATION_GOT_IP:
|
||||
return "STATION_GOT_IP";
|
||||
return "GOT_IP";
|
||||
default:
|
||||
return "Unknown connect status!!";
|
||||
}
|
||||
|
||||
@ -35,6 +35,13 @@
|
||||
extern void jshPrintBanner(void); // prints a debugging banner while we're in beta
|
||||
#endif
|
||||
|
||||
#ifdef FLASH_STR
|
||||
// debugging...
|
||||
#define os_printf os_printf_plus
|
||||
extern void os_printf_plus(char *fmt, ...);
|
||||
#endif
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
typedef enum {
|
||||
IS_NONE,
|
||||
@ -176,23 +183,50 @@ NO_INLINE void jsiConsolePrintChar(char data) {
|
||||
/**
|
||||
* \breif Send a NULL terminated string to the console.
|
||||
*/
|
||||
NO_INLINE void jsiConsolePrint(const char *str) {
|
||||
NO_INLINE void jsiConsolePrintString(const char *str) {
|
||||
while (*str) {
|
||||
if (*str == '\n') jsiConsolePrintChar('\r');
|
||||
jsiConsolePrintChar(*(str++));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FLASH_STR
|
||||
// internal version that copies str from flash to an internal buffer
|
||||
NO_INLINE void jsiConsolePrintString_int(const char *str) {
|
||||
os_printf("jsiConsolePrintString_int %p\n", str);
|
||||
size_t len = flash_strlen(str);
|
||||
os_printf("len = %ld\n", len);
|
||||
char buff[len+1];
|
||||
flash_strncpy(buff, str, len+1);
|
||||
os_printf("buflen = %ld\n", strlen(buff));
|
||||
jsiConsolePrintString(buff);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Perform a printf to the console.
|
||||
* Execute a printf command to the current JS console.
|
||||
*/
|
||||
#ifndef FLASH_STR
|
||||
void jsiConsolePrintf(const char *fmt, ...) {
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
|
||||
va_end(argp);
|
||||
}
|
||||
#else
|
||||
void jsiConsolePrintf_int(const char *fmt, ...) {
|
||||
os_printf("jsiConsolePrintf_int %p\n", fmt);
|
||||
// fmt is in flash and requires special aligned accesses
|
||||
size_t len = flash_strlen(fmt);
|
||||
char buff[len+1];
|
||||
flash_strncpy(buff, fmt, len+1);
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
vcbprintf((vcbprintf_callback)jsiConsolePrintString, 0, buff, argp);
|
||||
va_end(argp);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Print the contents of a string var from a character position until end of line (adding an extra ' ' to delete a character if there was one)
|
||||
void jsiConsolePrintStringVarUntilEOL(JsVar *v, size_t fromCharacter, size_t maxChars, bool andBackup) {
|
||||
@ -354,7 +388,7 @@ void jsiConsoleReturnInputLine() {
|
||||
}
|
||||
}
|
||||
void jsiConsolePrintPosition(struct JsLex *lex, size_t tokenPos) {
|
||||
jslPrintPosition((vcbprintf_callback)jsiConsolePrint, 0, lex, tokenPos);
|
||||
jslPrintPosition((vcbprintf_callback)jsiConsolePrintString, 0, lex, tokenPos);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1053,7 +1087,7 @@ void jsiAppendStringToInputLine(const char *strToAppend) {
|
||||
}
|
||||
inputCursorPos += strSize; // no need for jsiInputLineCursorMoved(); as we just appended
|
||||
if (jsiShowInputLine()) {
|
||||
jsiConsolePrint(strToAppend);
|
||||
jsiConsolePrintString(strToAppend);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2092,7 +2126,7 @@ void jsiDebuggerLoop() {
|
||||
while (lineLen < sizeof(lineStr)-1) lineStr[lineLen++]=' ';
|
||||
lineStr[lineLen] = 0;
|
||||
// print the line of code, prefixed by the line number, and with a pointer to the exact character in question
|
||||
jslPrintTokenLineMarker((vcbprintf_callback)jsiConsolePrint, 0, execInfo.lex, execInfo.lex->tokenLastStart, lineStr);
|
||||
jslPrintTokenLineMarker((vcbprintf_callback)jsiConsolePrintString, 0, execInfo.lex, execInfo.lex->tokenLastStart, lineStr);
|
||||
}
|
||||
|
||||
while (!(jsiStatus & JSIS_EXIT_DEBUGGER) &&
|
||||
|
||||
@ -66,10 +66,27 @@ void jsiSetConsoleDevice(IOEventFlags device);
|
||||
IOEventFlags jsiGetConsoleDevice();
|
||||
/// Transmit a byte
|
||||
void jsiConsolePrintChar(char data);
|
||||
/// Transmit a string
|
||||
void jsiConsolePrint(const char *str);
|
||||
/// Transmit a string (may be any string)
|
||||
void jsiConsolePrintString(const char *str);
|
||||
#ifndef FLASH_STR
|
||||
#define jsiConsolePrint jsiConsolePrintString
|
||||
/// Write the formatted string to the console (see vcbprintf)
|
||||
void jsiConsolePrintf(const char *fmt, ...);
|
||||
#else
|
||||
/// Write the formatted string to the console (see vcbprintf), but place the format string into
|
||||
// into flash
|
||||
#define jsiConsolePrintf(fmt, ...) do { \
|
||||
FLASH_STR(flash_str, fmt); \
|
||||
jsiConsolePrintf_int(flash_str, ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
void jsiConsolePrintf_int(const char *fmt, ...);
|
||||
/// Transmit a string (must be a literal string)
|
||||
#define jsiConsolePrint(str) do { \
|
||||
FLASH_STR(flash_str, str); \
|
||||
jsiConsolePrintString_int(flash_str); \
|
||||
} while(0)
|
||||
void jsiConsolePrintString_int(const char *str);
|
||||
#endif
|
||||
/// Print the contents of a string var - directly
|
||||
void jsiConsolePrintStringVar(JsVar *v);
|
||||
/// Transmit a position in the lexer (for reporting errors)
|
||||
|
||||
@ -545,13 +545,13 @@ NO_INLINE JsVar *jspeFunctionCall(JsVar *function, JsVar *functionName, JsVar *t
|
||||
execInfo.thisVar = jsvRef(thisVar);
|
||||
else {
|
||||
if (nativePtr==jswrap_eval) { // eval gets to use the current scope
|
||||
/* Note: proper JS has some utterly insane code that depends on whether
|
||||
/* Note: proper JS has some utterly insane code that depends on whether
|
||||
* eval is an lvalue or not:
|
||||
*
|
||||
*
|
||||
* http://stackoverflow.com/questions/9107240/1-evalthis-vs-evalthis-in-javascript
|
||||
*
|
||||
*
|
||||
* Doing this in Espruino is quite an upheaval for that one
|
||||
* slightly insane case - so it's not implemented. */
|
||||
* slightly insane case - so it's not implemented. */
|
||||
if (execInfo.thisVar) execInfo.thisVar = jsvRef(execInfo.thisVar);
|
||||
} else {
|
||||
execInfo.thisVar = jsvRef(execInfo.root); // 'this' should always default to root
|
||||
@ -731,7 +731,10 @@ NO_INLINE JsVar *jspeFunctionCall(JsVar *function, JsVar *functionName, JsVar *t
|
||||
bool hadDebuggerNextLineOnly = false;
|
||||
|
||||
if (execInfo.execute&EXEC_DEBUGGER_STEP_INTO) {
|
||||
jsiConsolePrintf(functionName ? "Stepping into %v\n" : "Stepping into function\n", functionName);
|
||||
if (functionName)
|
||||
jsiConsolePrintf("Stepping into %v\n", functionName);
|
||||
else
|
||||
jsiConsolePrintf("Stepping into function\n", functionName);
|
||||
} else {
|
||||
hadDebuggerNextLineOnly = execInfo.execute&EXEC_DEBUGGER_NEXT_LINE;
|
||||
if (hadDebuggerNextLineOnly)
|
||||
@ -1135,7 +1138,7 @@ NO_INLINE JsVar *jspeFactorFunctionCall() {
|
||||
} else
|
||||
a = jspeFunctionCall(func, funcName, parent, true, 0, 0);
|
||||
|
||||
jsvUnLock3(funcName, func, parent);
|
||||
jsvUnLock3(funcName, func, parent);
|
||||
parent=0;
|
||||
a = jspeFactorMember(a, &parent);
|
||||
}
|
||||
@ -1237,7 +1240,7 @@ NO_INLINE void jspEnsureIsPrototype(JsVar *instanceOf, JsVar *prototypeName) {
|
||||
JsVar *prototypeVar = jsvSkipName(prototypeName);
|
||||
if (!jsvIsObject(prototypeVar)) {
|
||||
if (!jsvIsUndefined(prototypeVar))
|
||||
jsWarn("Prototype is not an Object, so setting it to {}");
|
||||
jsWarn("Prototype is not an Object, so setting it to {}");
|
||||
jsvUnLock(prototypeVar);
|
||||
prototypeVar = jsvNewWithFlags(JSV_OBJECT); // prototype is supposed to be an object
|
||||
JsVar *lastName = jsvSkipToLastName(prototypeName);
|
||||
@ -1934,7 +1937,7 @@ NO_INLINE JsVar *jspeStatementFor() {
|
||||
// initialisation
|
||||
JsVar *forStatement = 0;
|
||||
// we could have 'for (;;)' - so don't munch up our semicolon if that's all we have
|
||||
if (execInfo.lex->tk != ';')
|
||||
if (execInfo.lex->tk != ';')
|
||||
forStatement = jspeStatement();
|
||||
if (jspIsInterrupted()) {
|
||||
jsvUnLock(forStatement);
|
||||
|
||||
119
src/jsutils.c
119
src/jsutils.c
@ -19,6 +19,12 @@
|
||||
#include "jswrap_error.h"
|
||||
#include "jswrap_json.h"
|
||||
|
||||
#ifdef FLASH_STR
|
||||
// debugging...
|
||||
#define os_printf os_printf_plus
|
||||
extern void os_printf_plus(char *fmt, ...);
|
||||
#endif
|
||||
|
||||
/** Error flags for things that we don't really want to report on the console,
|
||||
* but which are good to know about */
|
||||
JsErrorFlags jsErrorFlags;
|
||||
@ -163,15 +169,32 @@ long long stringToInt(const char *s) {
|
||||
}
|
||||
|
||||
|
||||
#ifndef FLASH_STR
|
||||
NO_INLINE void jsError(const char *fmt, ...) {
|
||||
jsiConsoleRemoveInputLine();
|
||||
jsiConsolePrint("ERROR: ");
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
|
||||
vcbprintf((vcbprintf_callback)jsiConsolePrintString,0, fmt, argp);
|
||||
va_end(argp);
|
||||
jsiConsolePrint("\n");
|
||||
}
|
||||
#else
|
||||
NO_INLINE void jsError_int(const char *fmt, ...) {
|
||||
os_printf("jsError_int %p\n", fmt);
|
||||
size_t len = flash_strlen(fmt);
|
||||
char buff[len+1];
|
||||
flash_strncpy(buff, fmt, len+1);
|
||||
|
||||
jsiConsoleRemoveInputLine();
|
||||
jsiConsolePrint("ERROR: ");
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
vcbprintf((vcbprintf_callback)jsiConsolePrintString,0, buff, argp);
|
||||
va_end(argp);
|
||||
jsiConsolePrint("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
NO_INLINE void jsExceptionHere(JsExceptionType type, const char *fmt, ...) {
|
||||
// If we already had an exception, forget this
|
||||
@ -215,20 +238,37 @@ NO_INLINE void jsExceptionHere(JsExceptionType type, const char *fmt, ...) {
|
||||
|
||||
|
||||
|
||||
#ifndef FLASH_STR
|
||||
NO_INLINE void jsWarn(const char *fmt, ...) {
|
||||
jsiConsoleRemoveInputLine();
|
||||
jsiConsolePrint("WARNING: ");
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
|
||||
vcbprintf((vcbprintf_callback)jsiConsolePrintString,0, fmt, argp);
|
||||
va_end(argp);
|
||||
jsiConsolePrint("\n");
|
||||
}
|
||||
#else
|
||||
NO_INLINE void jsWarn_int(const char *fmt, ...) {
|
||||
os_printf("jsWarn_int %p\n", fmt);
|
||||
size_t len = flash_strlen(fmt);
|
||||
char buff[len+1];
|
||||
flash_strncpy(buff, fmt, len+1);
|
||||
|
||||
jsiConsoleRemoveInputLine();
|
||||
jsiConsolePrint("WARNING: ");
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
vcbprintf((vcbprintf_callback)jsiConsolePrintString,0, buff, argp);
|
||||
va_end(argp);
|
||||
jsiConsolePrint("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
NO_INLINE void jsWarnAt(const char *message, struct JsLex *lex, size_t tokenPos) {
|
||||
jsiConsoleRemoveInputLine();
|
||||
jsiConsolePrint("WARNING: ");
|
||||
jsiConsolePrint(message);
|
||||
jsiConsolePrintString(message);
|
||||
if (lex) {
|
||||
jsiConsolePrint(" at ");
|
||||
jsiConsolePrintPosition(lex, tokenPos);
|
||||
@ -242,10 +282,22 @@ NO_INLINE void jsAssertFail(const char *file, int line, const char *expr) {
|
||||
inAssertFail = true;
|
||||
jsiConsoleRemoveInputLine();
|
||||
if (expr) {
|
||||
#ifndef FLASH_STR
|
||||
jsiConsolePrintf("ASSERT(%s) FAILED AT ", expr);
|
||||
} else
|
||||
#else
|
||||
os_printf("jsAssertFail %s:%ld\n", file, line);
|
||||
jsiConsolePrintString("ASSERT(");
|
||||
// string is in flash and requires word access, thus copy it onto the stack
|
||||
size_t len = flash_strlen(expr);
|
||||
char buff[len+1];
|
||||
flash_strncpy(buff, expr, len+1);
|
||||
jsiConsolePrintString(buff);
|
||||
jsiConsolePrintString(") FAILED AT ");
|
||||
#endif
|
||||
} else {
|
||||
jsiConsolePrint("ASSERT FAILED AT ");
|
||||
jsiConsolePrintf("%s:%d\n",file,line);
|
||||
}
|
||||
jsiConsolePrintf_int("%s:%d\n",file,line);
|
||||
if (!wasInAssertFail) {
|
||||
jsvTrace(jsvFindOrCreateRoot(), 2);
|
||||
}
|
||||
@ -264,6 +316,61 @@ NO_INLINE void jsAssertFail(const char *file, int line, const char *expr) {
|
||||
inAssertFail = false;
|
||||
}
|
||||
|
||||
#ifdef FLASH_STR
|
||||
// Helpers to deal with constant strings stored in flash that have to be accessed using word-aligned
|
||||
// and word-sized reads
|
||||
|
||||
// Get the length of a string in flash
|
||||
size_t flash_strlen(const char *str) {
|
||||
//os_printf("flash_strlen %p", str);
|
||||
size_t len = 0;
|
||||
uint32_t *s = (uint32_t *)str;
|
||||
|
||||
while (1) {
|
||||
uint32_t w = *s++;
|
||||
//os_printf(" %08lx", w);
|
||||
if ((w & 0xff) == 0) break;
|
||||
len++; w >>= 8;
|
||||
//os_printf(" %02lx-%02lx", w, w&0xff);
|
||||
if ((w & 0xff) == 0) break;
|
||||
len++; w >>= 8;
|
||||
if ((w & 0xff) == 0) break;
|
||||
len++; w >>= 8;
|
||||
if ((w & 0xff) == 0) break;
|
||||
len++;
|
||||
}
|
||||
//os_printf(" -> %ld\n", len);
|
||||
return len;
|
||||
}
|
||||
|
||||
// Copy a string from flash
|
||||
char *flash_strncpy(char *dst, const char *src, size_t c) {
|
||||
char *d = dst;
|
||||
uint32_t *s = (uint32_t *)src;
|
||||
size_t slen = flash_strlen(src);
|
||||
size_t len = slen > c ? c : slen;
|
||||
// copy full words from source string
|
||||
while (len >= 4) {
|
||||
uint32_t w = *s++;
|
||||
*d++ = w & 0xff; w >>= 8;
|
||||
*d++ = w & 0xff; w >>= 8;
|
||||
*d++ = w & 0xff; w >>= 8;
|
||||
*d++ = w & 0xff;
|
||||
len -= 4;
|
||||
}
|
||||
// copy any remaining bytes
|
||||
if (len > 0) {
|
||||
uint32_t w = *s++;
|
||||
while (len-- > 0) {
|
||||
*d++ = w & 0xff; w >>= 8;
|
||||
}
|
||||
}
|
||||
// terminating null
|
||||
if (slen < c) *d = 0;
|
||||
return dst;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FAKE_STDLIB
|
||||
char * strncat(char *dst, const char *src, size_t c) {
|
||||
char *dstx = dst;
|
||||
@ -466,7 +573,7 @@ void ftoa_bounded_extra(JsVarFloat val,char *str, size_t len, int radix, int fra
|
||||
val = -val;
|
||||
}
|
||||
|
||||
// what if we're really close to an integer? Just use that...
|
||||
// what if we're really close to an integer? Just use that...
|
||||
if (((JsVarInt)(val+stopAtError)) == (1+(JsVarInt)val))
|
||||
val = (JsVarFloat)(1+(JsVarInt)val);
|
||||
|
||||
|
||||
@ -54,6 +54,18 @@ extern int isfinite ( double );
|
||||
#define alloca(x) __builtin_alloca(x)
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
// Place constant strings into flash when we can in order to same RAM space. Strings in flash
|
||||
// must be accessed with word reads on aligned boundaries, so we'll have to copy them before
|
||||
// regular use.
|
||||
#define FLASH_STR(name, x) static const char name[] __attribute__((section(".irom.text"))) __attribute__((aligned(4))) = x
|
||||
|
||||
// Get the length of a string in flash
|
||||
size_t flash_strlen(const char *str);
|
||||
// Copy a string from flash
|
||||
char *flash_strncpy(char *dest, const char *source, size_t cap);
|
||||
#endif
|
||||
|
||||
#if !defined(__USB_TYPE_H) && !defined(CPLUSPLUS) && !defined(__cplusplus) // it is defined in this file too!
|
||||
#undef FALSE
|
||||
#undef TRUE
|
||||
@ -74,13 +86,13 @@ extern int isfinite ( double );
|
||||
#define DBL_MIN 2.2250738585072014e-308
|
||||
#define DBL_MAX 1.7976931348623157e+308
|
||||
|
||||
/* Number of Js Variables allowed and Js Reference format.
|
||||
/* Number of Js Variables allowed and Js Reference format.
|
||||
|
||||
JsVarRef = uint8_t -> 15 bytes/JsVar so JSVAR_CACHE_SIZE = (RAM - 3000) / 15
|
||||
JsVarRef = uint16_t -> 20 bytes/JsVar so JSVAR_CACHE_SIZE = (RAM - 3000) / 20
|
||||
JsVarRef = uint32_t -> 26 bytes/JsVar so JSVAR_CACHE_SIZE = (RAM - 3000) / 26
|
||||
|
||||
NOTE: JSVAR_CACHE_SIZE must be at least 2 less than the number we can fit in JsVarRef
|
||||
NOTE: JSVAR_CACHE_SIZE must be at least 2 less than the number we can fit in JsVarRef
|
||||
See jshardware.c FLASH constants - all this must be able to fit in flash
|
||||
|
||||
|
||||
@ -199,17 +211,22 @@ typedef int64_t JsSysTime;
|
||||
#define JSPARSE_MODULE_CACHE_NAME "modules"
|
||||
|
||||
#if !defined(NO_ASSERT)
|
||||
#ifdef __STRING
|
||||
#define assert(X) if (!(X)) jsAssertFail(__FILE__,__LINE__,__STRING(X));
|
||||
#ifdef FLASH_STR
|
||||
#define assert(X) do { \
|
||||
FLASH_STR(flash_X, __STRING(X)); \
|
||||
if (!(X)) jsAssertFail(__FILE__,__LINE__,flash_X); \
|
||||
} while(0)
|
||||
#elif defined(__STRING)
|
||||
#define assert(X) do { if (!(X)) jsAssertFail(__FILE__,__LINE__,__STRING(X)) } while(0)
|
||||
#else
|
||||
#define assert(X) if (!(X)) jsAssertFail(__FILE__,__LINE__,"");
|
||||
#define assert(X) do { if (!(X)) jsAssertFail(__FILE__,__LINE__,"") } while(0)
|
||||
#endif
|
||||
#else
|
||||
#define assert(X) {}
|
||||
#define assert(X) do { } while(0)
|
||||
#endif
|
||||
|
||||
/// Used when we have enums we want to squash down
|
||||
#define PACKED_FLAGS __attribute__ ((__packed__))
|
||||
#define PACKED_FLAGS __attribute__ ((__packed__))
|
||||
|
||||
/// Used before functions that we want to ensure are not inlined (eg. "void NO_INLINE foo() {}")
|
||||
#define NO_INLINE __attribute__ ((noinline))
|
||||
@ -286,7 +303,7 @@ static inline bool isAlpha(char ch) {
|
||||
|
||||
bool isIDString(const char *s);
|
||||
|
||||
/** escape a character - if it is required. This may return a reference to a static array,
|
||||
/** escape a character - if it is required. This may return a reference to a static array,
|
||||
so you can't store the value it returns in a variable and call it again. */
|
||||
const char *escapeCharacter(char ch);
|
||||
/// Convert a character to the hexadecimal equivalent (or -1)
|
||||
@ -308,11 +325,25 @@ typedef enum {
|
||||
JSET_REFERENCEERROR
|
||||
} JsExceptionType;
|
||||
|
||||
void jsError(const char *fmt, ...);
|
||||
void jsExceptionHere(JsExceptionType type, const char *fmt, ...);
|
||||
void jsWarn(const char *fmt, ...);
|
||||
void jsWarnAt(const char *message, struct JsLex *lex, size_t tokenPos);
|
||||
void jsAssertFail(const char *file, int line, const char *expr);
|
||||
#ifndef FLASH_STR
|
||||
void jsError(const char *fmt, ...);
|
||||
void jsWarn(const char *fmt, ...);
|
||||
#else
|
||||
#define jsError(fmt, ...) do { \
|
||||
FLASH_STR(flash_str, fmt); \
|
||||
jsError_int(flash_str, ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
void jsError_int(const char *fmt, ...);
|
||||
|
||||
#define jsWarn(fmt, ...) do { \
|
||||
FLASH_STR(flash_str, fmt); \
|
||||
jsWarn_int(flash_str, ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
void jsWarn_int(const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
// ------------
|
||||
typedef enum {
|
||||
|
||||
12
src/jsvar.c
12
src/jsvar.c
@ -131,8 +131,8 @@ void jsvSetPrevSibling(JsVar *v, JsVarRef r) {
|
||||
v->varData.ref.pack = (unsigned char)((v->varData.ref.pack & ~(JSVARREF_PACKED_BIT_MASK<<(JSVARREF_PACKED_BITS*3))) | (((r >> 8) & JSVARREF_PACKED_BIT_MASK) << (JSVARREF_PACKED_BITS*3)));
|
||||
}
|
||||
/* lastchild stores the upper 2 bits in JsVarFlags because then STRING_EXT can use one more character! */
|
||||
JsVarRef jsvGetLastChild(const JsVar *v) {
|
||||
return (JsVarRef)(v->varData.ref.lastChild | (((v->flags >> JSV_LASTCHILD_BIT_SHIFT)&JSVARREF_PACKED_BIT_MASK))<<8);
|
||||
JsVarRef jsvGetLastChild(const JsVar *v) {
|
||||
return (JsVarRef)(v->varData.ref.lastChild | (((v->flags >> JSV_LASTCHILD_BIT_SHIFT)&JSVARREF_PACKED_BIT_MASK))<<8);
|
||||
}
|
||||
void jsvSetLastChild(JsVar *v, JsVarRef r) {
|
||||
v->varData.ref.lastChild = (unsigned char)(r & 0xFF);
|
||||
@ -945,7 +945,7 @@ const char *jsvGetConstString(const JsVar *v) {
|
||||
/// Return the 'type' of the JS variable (eg. JS's typeof operator)
|
||||
const char *jsvGetTypeOf(const JsVar *v) {
|
||||
if (jsvIsUndefined(v)) return "undefined";
|
||||
if (jsvIsNull(v) || jsvIsObject(v) ||
|
||||
if (jsvIsNull(v) || jsvIsObject(v) ||
|
||||
jsvIsArray(v) || jsvIsArrayBuffer(v)) return "object";
|
||||
if (jsvIsFunction(v)) return "function";
|
||||
if (jsvIsString(v)) return "string";
|
||||
@ -1109,10 +1109,10 @@ JsVar *jsvAsFlatString(JsVar *var) {
|
||||
jsvStringIteratorNew(&src, str, 0);
|
||||
jsvStringIteratorNew(&dst, flat, 0);
|
||||
while (len--) {
|
||||
jsvStringIteratorSetChar(&dst, jsvStringIteratorGetChar(&src));
|
||||
jsvStringIteratorSetChar(&dst, jsvStringIteratorGetChar(&src));
|
||||
if (len>0) {
|
||||
jsvStringIteratorNext(&src);
|
||||
jsvStringIteratorNext(&dst);
|
||||
jsvStringIteratorNext(&dst);
|
||||
}
|
||||
}
|
||||
jsvStringIteratorFree(&src);
|
||||
@ -2435,7 +2435,7 @@ JsVar *jsvGetArrayIndexOf(JsVar *arr, JsVar *value, bool matchExact) {
|
||||
indexref = jsvGetFirstChild(arr);
|
||||
while (indexref) {
|
||||
JsVar *childIndex = jsvLock(indexref);
|
||||
assert(jsvIsName(childIndex))
|
||||
assert(jsvIsName(childIndex));
|
||||
JsVar *childValue = jsvSkipName(childIndex);
|
||||
if (childValue==value ||
|
||||
(!matchExact && jsvMathsOpTypeEqual(childValue, value))) {
|
||||
|
||||
@ -121,7 +121,7 @@ void jswrap_interface_trace(JsVar *root) {
|
||||
/*JSON{
|
||||
"type" : "function",
|
||||
"name" : "dump",
|
||||
"generate_full" : "jsiDumpState((vcbprintf_callback)jsiConsolePrint, 0)"
|
||||
"generate_full" : "jsiDumpState((vcbprintf_callback)jsiConsolePrintString, 0)"
|
||||
}
|
||||
Output current interpreter state in a text form such that it can be copied to a new device
|
||||
|
||||
@ -210,7 +210,7 @@ void jswrap_interface_print(JsVar *v) {
|
||||
jsvObjectIteratorNew(&it, v);
|
||||
while (jsvObjectIteratorHasValue(&it)) {
|
||||
JsVar *v = jsvObjectIteratorGetValue(&it);
|
||||
if (jsvIsString(v))
|
||||
if (jsvIsString(v))
|
||||
jsiConsolePrintStringVar(v);
|
||||
else
|
||||
jsfPrintJSON(v, JSON_PRETTY | JSON_NEWLINES);
|
||||
|
||||
@ -372,8 +372,8 @@ void jsfGetJSON(JsVar *var, JsVar *result, JSONFlags flags) {
|
||||
}
|
||||
|
||||
void jsfPrintJSON(JsVar *var, JSONFlags flags) {
|
||||
jsfGetJSONWithCallback(var, flags, (vcbprintf_callback)jsiConsolePrint, 0);
|
||||
jsfGetJSONWithCallback(var, flags, (vcbprintf_callback)jsiConsolePrintString, 0);
|
||||
}
|
||||
void jsfPrintJSONForFunction(JsVar *var, JSONFlags flags) {
|
||||
jsfGetJSONForFunctionWithCallback(var, flags, (vcbprintf_callback)jsiConsolePrint, 0);
|
||||
jsfGetJSONForFunctionWithCallback(var, flags, (vcbprintf_callback)jsiConsolePrintString, 0);
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ typedef long long int64_t;
|
||||
#define TASK_QUEUE_LENGTH 10
|
||||
|
||||
// Should we introduce a ticker to say we are still alive?
|
||||
//#define EPS8266_BOARD_HEARTBEAT
|
||||
#define EPS8266_BOARD_HEARTBEAT
|
||||
|
||||
// --- Forward definitions
|
||||
static void mainLoop();
|
||||
@ -117,6 +117,8 @@ void jshPrintBanner() {
|
||||
uint32_t fid = spi_flash_get_id();
|
||||
uint32_t chip = (fid&0xff00)|((fid>>16)&0xff);
|
||||
uint32_t map = system_get_flash_size_map();
|
||||
os_printf("Espruino "JS_VERSION"\nFlash map %s, manuf 0x%lx chip 0x%lx\n",
|
||||
flash_maps[map], fid & 0xff, chip);
|
||||
jsiConsolePrintf(
|
||||
"WARNING: the esp8266 port is in beta, don't expect everything to work\n"
|
||||
"Flash map %s, manuf 0x%x chip 0x%x\n",
|
||||
@ -126,7 +128,6 @@ void jshPrintBanner() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Queue a task for the main loop.
|
||||
*/
|
||||
@ -216,8 +217,8 @@ static void mainLoop() {
|
||||
#ifdef EPS8266_BOARD_HEARTBEAT
|
||||
if (system_get_time() - lastTime > 1000 * 1000 * 5) {
|
||||
lastTime = system_get_time();
|
||||
os_printf("tick: %u, heap: %u\n",
|
||||
(uint32)(jshGetSystemTime()), system_get_free_heap_size());
|
||||
os_printf("tick: %ums, heap: %u\n",
|
||||
(uint32)(jshGetSystemTime())/1000, system_get_free_heap_size());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user