esp8266: move lots of string constants to flash

This commit is contained in:
Thorsten von Eicken 2015-11-23 17:42:10 -08:00
parent a2d4048fe6
commit 1be478dd32
11 changed files with 309 additions and 80 deletions

View File

@ -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")

View File

@ -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!!";
}

View File

@ -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) &&

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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 {

View File

@ -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))) {

View File

@ -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);

View File

@ -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);
}

View File

@ -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