Compare commits

...

6 Commits

Author SHA1 Message Date
Xuan-Son Nguyen
06f545e65e
Merge 49d2c5eba8 into e5c0ddc9fa 2026-02-01 12:53:24 +01:00
Uri Tauber
e5c0ddc9fa
feat: Debugging monitor script (#555)
Some checks are pending
CI / build (push) Waiting to run
## Summary

* **What is the goal of this PR?**
Add a debugging script to help developers monitor the ESP32 serial port
directly from a PC.

* **What changes are included?**
Added a new script: scripts/debugging_monitor.py

## Additional Context

While working on a new Crosspoint-Reader feature, it quickly became
clear that watching the ESP32 serial output without any visual cues was
inconvenient and easy to mess up.

This script improves the debugging experience by reading data from the
serial port and providing:

1. A timestamp prefix for every log line (instead of milliseconds since
power-up)
2. Color-coded output for different message types
3. A secondary window displaying a live graph of RAM usage, which is
especially useful for tracking the memory impact of new features

<img width="1916" height="1049" alt="Screenshot_20260126_183811"
src="https://github.com/user-attachments/assets/6291887f-ac17-43ac-9e43-f5dec8a7097e"
/>

---

### AI Usage

Did you use AI tools to help write this code? _**< PARTIALLY >**_
I wrote the initial version of the script. Gemini was used to help add
the Matplotlib-based graphing and threading logic.
2026-02-01 22:53:20 +11:00
Arthur Tazhitdinov
b1dcb7733b
fix: truncating chapter titles using UTF-8 safe function (#599)
## Summary

* Truncating chapter titles using utf8 safe functions (Cyrillic titles
were split mid codepoint)
* refactoring of lib/Utf8

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**< PARTIALLY >**_
2026-02-01 22:23:48 +11:00
Arthur Tazhitdinov
0d82b03981
fix: don't wake up after USB connect (#644)
## Summary

* fixes problem that if short power button press is enabled, connecting
device to usb leads to waking up
2026-02-01 22:19:33 +11:00
Dave Allie
5a97334ace
Revert "fix: don't wake up after USB connect" (#643)
Reverts crosspoint-reader/crosspoint-reader#576

Causing a boot loop on master
2026-02-01 21:35:25 +11:00
Xuan Son Nguyen
49d2c5eba8 init version 2026-01-31 22:01:10 +01:00
40 changed files with 32216 additions and 44 deletions

View File

@ -95,6 +95,20 @@ Connect your Xteink X4 to your computer via USB-C and run the following command.
```sh ```sh
pio run --target upload pio run --target upload
``` ```
### Debugging
After flashing the new features, its recommended to capture detailed logs from the serial port.
First, make sure all required Python packages are installed:
```python
python3 -m pip install serial colorama matplotlib
```
after that run the script:
```sh
python3 scripts/debugging_monitor.py
```
This was tested on Debian and should work on most Linux systems. Minor adjustments may be required for Windows or macOS.
## Internals ## Internals

View File

@ -415,13 +415,21 @@ void GfxRenderer::displayBuffer(const HalDisplay::RefreshMode refreshMode) const
std::string GfxRenderer::truncatedText(const int fontId, const char* text, const int maxWidth, std::string GfxRenderer::truncatedText(const int fontId, const char* text, const int maxWidth,
const EpdFontFamily::Style style) const { const EpdFontFamily::Style style) const {
if (!text || maxWidth <= 0) return "";
std::string item = text; std::string item = text;
int itemWidth = getTextWidth(fontId, item.c_str(), style); const char* ellipsis = "...";
while (itemWidth > maxWidth && item.length() > 8) { int textWidth = getTextWidth(fontId, item.c_str(), style);
item.replace(item.length() - 5, 5, "..."); if (textWidth <= maxWidth) {
itemWidth = getTextWidth(fontId, item.c_str(), style); // Text fits, return as is
return item;
} }
return item;
while (!item.empty() && getTextWidth(fontId, (item + ellipsis).c_str(), style) >= maxWidth) {
utf8RemoveLastChar(item);
}
return item.empty() ? ellipsis : item + ellipsis;
} }
// Note: Internal driver treats screen in command orientation; this library exposes a logical orientation // Note: Internal driver treats screen in command orientation; this library exposes a logical orientation

View File

@ -29,3 +29,20 @@ uint32_t utf8NextCodepoint(const unsigned char** string) {
return cp; return cp;
} }
size_t utf8RemoveLastChar(std::string& str) {
if (str.empty()) return 0;
size_t pos = str.size() - 1;
while (pos > 0 && (static_cast<unsigned char>(str[pos]) & 0xC0) == 0x80) {
--pos;
}
str.resize(pos);
return pos;
}
// Truncate string by removing N UTF-8 characters from the end
void utf8TruncateChars(std::string& str, const size_t numChars) {
for (size_t i = 0; i < numChars && !str.empty(); ++i) {
utf8RemoveLastChar(str);
}
}

View File

@ -1,7 +1,11 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <string>
#define REPLACEMENT_GLYPH 0xFFFD #define REPLACEMENT_GLYPH 0xFFFD
uint32_t utf8NextCodepoint(const unsigned char** string); uint32_t utf8NextCodepoint(const unsigned char** string);
// Remove the last UTF-8 codepoint from a std::string and return the new size.
size_t utf8RemoveLastChar(std::string& str);
// Truncate string by removing N UTF-8 codepoints from the end.
void utf8TruncateChars(std::string& str, size_t numChars);

22
lib/mquickjs/LICENSE Normal file
View File

@ -0,0 +1,22 @@
Micro QuickJS Javascript Engine
Copyright (c) 2017-2025 Fabrice Bellard
Copyright (c) 2017-2025 Charlie Gordon
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.

158
lib/mquickjs/Makefile Normal file
View File

@ -0,0 +1,158 @@
#CONFIG_PROFILE=y
#CONFIG_X86_32=y
#CONFIG_ARM32=y
#CONFIG_WIN32=y
#CONFIG_SOFTFLOAT=y
#CONFIG_ASAN=y
#CONFIG_GPROF=y
CONFIG_SMALL=y
# consider warnings as errors (for development)
#CONFIG_WERROR=y
ifdef CONFIG_ARM32
CROSS_PREFIX=arm-linux-gnu-
endif
ifdef CONFIG_WIN32
ifdef CONFIG_X86_32
CROSS_PREFIX?=i686-w64-mingw32-
else
CROSS_PREFIX?=x86_64-w64-mingw32-
endif
EXE=.exe
else
CROSS_PREFIX?=
EXE=
endif
HOST_CC=gcc
CC=$(CROSS_PREFIX)gcc
CFLAGS=-Wall -g -MMD -D_GNU_SOURCE -fno-math-errno -fno-trapping-math
HOST_CFLAGS=-Wall -g -MMD -D_GNU_SOURCE -fno-math-errno -fno-trapping-math
ifdef CONFIG_WERROR
CFLAGS+=-Werror
HOST_CFLAGS+=-Werror
endif
ifdef CONFIG_ARM32
CFLAGS+=-mthumb
endif
ifdef CONFIG_SMALL
CFLAGS+=-Os
else
CFLAGS+=-O2
endif
#CFLAGS+=-fstack-usage
ifdef CONFIG_SOFTFLOAT
CFLAGS+=-msoft-float
CFLAGS+=-DUSE_SOFTFLOAT
endif # CONFIG_SOFTFLOAT
HOST_CFLAGS+=-O2
LDFLAGS=-g
HOST_LDFLAGS=-g
ifdef CONFIG_GPROF
CFLAGS+=-p
LDFLAGS+=-p
endif
ifdef CONFIG_ASAN
CFLAGS+=-fsanitize=address -fno-omit-frame-pointer
LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer
endif
ifdef CONFIG_X86_32
CFLAGS+=-m32
LDFLAGS+=-m32
endif
ifdef CONFIG_PROFILE
CFLAGS+=-p
LDFLAGS+=-p
endif
# when cross compiling from a 64 bit system to a 32 bit system, force
# a 32 bit output
ifdef CONFIG_X86_32
MQJS_BUILD_FLAGS=-m32
endif
ifdef CONFIG_ARM32
MQJS_BUILD_FLAGS=-m32
endif
PROGS=mqjs$(EXE) example$(EXE)
TEST_PROGS=dtoa_test libm_test
all: $(PROGS)
MQJS_OBJS=mqjs.o readline_tty.o readline.o mquickjs.o dtoa.o libm.o cutils.o
LIBS=-lm
mqjs$(EXE): $(MQJS_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
mquickjs.o: mquickjs_atom.h
mqjs_stdlib: mqjs_stdlib.host.o mquickjs_build.host.o
$(HOST_CC) $(HOST_LDFLAGS) -o $@ $^
mquickjs_atom.h: mqjs_stdlib
./mqjs_stdlib -a $(MQJS_BUILD_FLAGS) > $@
mqjs_stdlib.h: mqjs_stdlib
./mqjs_stdlib $(MQJS_BUILD_FLAGS) > $@
mqjs.o: mqjs_stdlib.h
# C API example
example.o: example_stdlib.h
example$(EXE): example.o mquickjs.o dtoa.o libm.o cutils.o
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
example_stdlib: example_stdlib.host.o mquickjs_build.host.o
$(HOST_CC) $(HOST_LDFLAGS) -o $@ $^
example_stdlib.h: example_stdlib
./example_stdlib $(MQJS_BUILD_FLAGS) > $@
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
%.host.o: %.c
$(HOST_CC) $(HOST_CFLAGS) -c -o $@ $<
test: mqjs example
./mqjs tests/test_closure.js
./mqjs tests/test_language.js
./mqjs tests/test_loop.js
./mqjs tests/test_builtin.js
# test bytecode generation and loading
./mqjs -o test_builtin.bin tests/test_builtin.js
# @sha256sum -c test_builtin.sha256
./mqjs -b test_builtin.bin
./example tests/test_rect.js
microbench: mqjs
./mqjs tests/microbench.js
octane: mqjs
./mqjs --memory-limit 256M tests/octane/run.js
size: mqjs
size mqjs mqjs.o readline.o cutils.o dtoa.o libm.o mquickjs.o
dtoa_test: tests/dtoa_test.o dtoa.o cutils.o tests/gay-fixed.o tests/gay-precision.o tests/gay-shortest.o
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
libm_test: tests/libm_test.o libm.o
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
rempio2_test: tests/rempio2_test.o libm.o
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
clean:
rm -f *.o *.d *~ tests/*.o tests/*.d tests/*~ test_builtin.bin mqjs_stdlib mqjs_stdlib.h mquickjs_build_atoms mquickjs_atom.h mqjs_example example_stdlib example_stdlib.h $(PROGS) $(TEST_PROGS)
-include $(wildcard *.d)
# ADDED FOR CROSSPOINT
mqjs_stdlib.host.o: scripts/crosspoint_stdlib.c
$(HOST_CC) $(HOST_CFLAGS) -c -o $@ $<
# END OF ADDED PART

File diff suppressed because it is too large Load Diff

178
lib/mquickjs/cutils.c Normal file
View File

@ -0,0 +1,178 @@
/*
* C utilities
*
* Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon
*
* 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 <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "cutils.h"
void pstrcpy(char *buf, int buf_size, const char *str)
{
int c;
char *q = buf;
if (buf_size <= 0)
return;
for(;;) {
c = *str++;
if (c == 0 || q >= buf + buf_size - 1)
break;
*q++ = c;
}
*q = '\0';
}
/* strcat and truncate. */
char *pstrcat(char *buf, int buf_size, const char *s)
{
int len;
len = strlen(buf);
if (len < buf_size)
pstrcpy(buf + len, buf_size - len, s);
return buf;
}
int strstart(const char *str, const char *val, const char **ptr)
{
const char *p, *q;
p = str;
q = val;
while (*q != '\0') {
if (*p != *q)
return 0;
p++;
q++;
}
if (ptr)
*ptr = p;
return 1;
}
int has_suffix(const char *str, const char *suffix)
{
size_t len = strlen(str);
size_t slen = strlen(suffix);
return (len >= slen && !memcmp(str + len - slen, suffix, slen));
}
size_t __unicode_to_utf8(uint8_t *buf, unsigned int c)
{
uint8_t *q = buf;
if (c < 0x800) {
*q++ = (c >> 6) | 0xc0;
} else {
if (c < 0x10000) {
*q++ = (c >> 12) | 0xe0;
} else {
if (c < 0x00200000) {
*q++ = (c >> 18) | 0xf0;
} else {
return 0;
}
*q++ = ((c >> 12) & 0x3f) | 0x80;
}
*q++ = ((c >> 6) & 0x3f) | 0x80;
}
*q++ = (c & 0x3f) | 0x80;
return q - buf;
}
int __unicode_from_utf8(const uint8_t *p, size_t max_len, size_t *plen)
{
size_t len = 1;
int c;
c = p[0];
if (c < 0xc0) {
goto fail;
} else if (c < 0xe0) {
if (unlikely(max_len < 2 || (p[1] & 0xc0) != 0x80))
goto fail;
c = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f);
len = 2;
if (unlikely(c < 0x80))
goto fail;
} else if (c < 0xf0) {
if (unlikely(max_len < 2 || (p[1] & 0xc0) != 0x80))
goto fail;
if (unlikely(max_len < 3 || (p[2] & 0xc0) != 0x80)) {
len = 2;
goto fail;
}
c = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
len = 3;
if (unlikely(c < 0x800))
goto fail;
} else if (c < 0xf8) {
if (unlikely(max_len < 2 || (p[1] & 0xc0) != 0x80))
goto fail;
if (unlikely(max_len < 3 || (p[2] & 0xc0) != 0x80)) {
len = 2;
goto fail;
}
if (unlikely(max_len < 4 || (p[3] & 0xc0) != 0x80)) {
len = 3;
goto fail;
}
c = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | ((p[2] & 0x3f) << 6) | (p[3] & 0x3f);
len = 4;
/* We explicitly accept surrogate pairs */
if (unlikely(c < 0x10000 || c > 0x10ffff))
goto fail;
} else {
fail:
*plen = len;
return -1;
}
*plen = len;
return c;
}
int __utf8_get(const uint8_t *p, size_t *plen)
{
size_t len;
int c;
c = p[0];
if (c < 0xc0) {
len = 1;
} else if (c < 0xe0) {
c = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f);
len = 2;
} else if (c < 0xf0) {
c = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
len = 3;
} else if (c < 0xf8) {
c = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | ((p[2] & 0x3f) << 6) | (p[3] & 0x3f);
len = 4;
} else {
len = 1;
}
*plen = len;
return c;
}

355
lib/mquickjs/cutils.h Normal file
View File

@ -0,0 +1,355 @@
/*
* C utilities
*
* Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon
*
* 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 CUTILS_H
#define CUTILS_H
#include <stdlib.h>
#include <inttypes.h>
/* set if CPU is big endian */
#undef WORDS_BIGENDIAN
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define force_inline inline __attribute__((always_inline))
#define no_inline __attribute__((noinline))
#define __maybe_unused __attribute__((unused))
#define xglue(x, y) x ## y
#define glue(x, y) xglue(x, y)
#define stringify(s) tostring(s)
#define tostring(s) #s
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif
#ifndef countof
#define countof(x) (sizeof(x) / sizeof((x)[0]))
#endif
/* return the pointer of type 'type *' containing 'ptr' as field 'member' */
#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))
typedef int BOOL;
#ifndef FALSE
enum {
FALSE = 0,
TRUE = 1,
};
#endif
void pstrcpy(char *buf, int buf_size, const char *str);
char *pstrcat(char *buf, int buf_size, const char *s);
int strstart(const char *str, const char *val, const char **ptr);
int has_suffix(const char *str, const char *suffix);
static inline int max_int(int a, int b)
{
if (a > b)
return a;
else
return b;
}
static inline int min_int(int a, int b)
{
if (a < b)
return a;
else
return b;
}
static inline uint32_t max_uint32(uint32_t a, uint32_t b)
{
if (a > b)
return a;
else
return b;
}
static inline uint32_t min_uint32(uint32_t a, uint32_t b)
{
if (a < b)
return a;
else
return b;
}
static inline int64_t max_int64(int64_t a, int64_t b)
{
if (a > b)
return a;
else
return b;
}
static inline int64_t min_int64(int64_t a, int64_t b)
{
if (a < b)
return a;
else
return b;
}
static inline size_t max_size_t(size_t a, size_t b)
{
if (a > b)
return a;
else
return b;
}
static inline size_t min_size_t(size_t a, size_t b)
{
if (a < b)
return a;
else
return b;
}
/* WARNING: undefined if a = 0 */
static inline int clz32(unsigned int a)
{
return __builtin_clz(a);
}
/* WARNING: undefined if a = 0 */
static inline int clz64(uint64_t a)
{
return __builtin_clzll(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz32(unsigned int a)
{
return __builtin_ctz(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz64(uint64_t a)
{
return __builtin_ctzll(a);
}
struct __attribute__((packed)) packed_u64 {
uint64_t v;
};
struct __attribute__((packed)) packed_u32 {
uint32_t v;
};
struct __attribute__((packed)) packed_u16 {
uint16_t v;
};
static inline uint64_t get_u64(const uint8_t *tab)
{
return ((const struct packed_u64 *)tab)->v;
}
static inline int64_t get_i64(const uint8_t *tab)
{
return (int64_t)((const struct packed_u64 *)tab)->v;
}
static inline void put_u64(uint8_t *tab, uint64_t val)
{
((struct packed_u64 *)tab)->v = val;
}
static inline uint32_t get_u32(const uint8_t *tab)
{
return ((const struct packed_u32 *)tab)->v;
}
static inline int32_t get_i32(const uint8_t *tab)
{
return (int32_t)((const struct packed_u32 *)tab)->v;
}
static inline void put_u32(uint8_t *tab, uint32_t val)
{
((struct packed_u32 *)tab)->v = val;
}
static inline uint32_t get_u16(const uint8_t *tab)
{
return ((const struct packed_u16 *)tab)->v;
}
static inline int32_t get_i16(const uint8_t *tab)
{
return (int16_t)((const struct packed_u16 *)tab)->v;
}
static inline void put_u16(uint8_t *tab, uint16_t val)
{
((struct packed_u16 *)tab)->v = val;
}
static inline uint32_t get_u8(const uint8_t *tab)
{
return *tab;
}
static inline int32_t get_i8(const uint8_t *tab)
{
return (int8_t)*tab;
}
static inline void put_u8(uint8_t *tab, uint8_t val)
{
*tab = val;
}
static inline uint16_t bswap16(uint16_t x)
{
return (x >> 8) | (x << 8);
}
static inline uint32_t bswap32(uint32_t v)
{
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
}
static inline uint64_t bswap64(uint64_t v)
{
return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) |
((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) |
((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) |
((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) |
((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) |
((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) |
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
}
static inline uint32_t get_be32(const uint8_t *d)
{
return bswap32(get_u32(d));
}
static inline void put_be32(uint8_t *d, uint32_t v)
{
put_u32(d, bswap32(v));
}
#define UTF8_CHAR_LEN_MAX 4
size_t __unicode_to_utf8(uint8_t *buf, unsigned int c);
int __unicode_from_utf8(const uint8_t *p, size_t max_len, size_t *plen);
int __utf8_get(const uint8_t *p, size_t *plen);
/* Note: at most 21 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes
are output. */
static inline size_t unicode_to_utf8(uint8_t *buf, unsigned int c)
{
if (c < 0x80) {
buf[0] = c;
return 1;
} else {
return __unicode_to_utf8(buf, c);
}
}
/* return -1 in case of error. Surrogates are accepted. max_len must
be >= 1. *plen is set in case of error and always >= 1. */
static inline int unicode_from_utf8(const uint8_t *buf, size_t max_len, size_t *plen)
{
if (buf[0] < 0x80) {
*plen = 1;
return buf[0];
} else {
return __unicode_from_utf8(buf, max_len, plen);
}
}
/* Warning: no error checking is done so the UTF-8 encoding must be
validated before. */
static force_inline int utf8_get(const uint8_t *buf, size_t *plen)
{
if (likely(buf[0] < 0x80)) {
*plen = 1;
return buf[0];
} else {
return __utf8_get(buf, plen);
}
}
static inline int from_hex(int c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else
return -1;
}
static inline uint64_t float64_as_uint64(double d)
{
union {
double d;
uint64_t u64;
} u;
u.d = d;
return u.u64;
}
static inline double uint64_as_float64(uint64_t u64)
{
union {
double d;
uint64_t u64;
} u;
u.u64 = u64;
return u.d;
}
typedef union {
uint32_t u32;
float f;
} f32_union;
static inline uint32_t float_as_uint(float f)
{
f32_union u;
u.f = f;
return u.u32;
}
static inline float uint_as_float(uint32_t v)
{
f32_union u;
u.u32 = v;
return u.f;
}
#endif /* CUTILS_H */

1620
lib/mquickjs/dtoa.c Normal file

File diff suppressed because it is too large Load Diff

83
lib/mquickjs/dtoa.h Normal file
View File

@ -0,0 +1,83 @@
/*
* Tiny float64 printing and parsing library
*
* Copyright (c) 2024-2025 Fabrice Bellard
*
* 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.
*/
//#define JS_DTOA_DUMP_STATS
/* maximum number of digits for fixed and frac formats */
#define JS_DTOA_MAX_DIGITS 101
/* radix != 10 is only supported with flags = JS_DTOA_FORMAT_FREE */
/* use as many digits as necessary */
#define JS_DTOA_FORMAT_FREE (0 << 0)
/* use n_digits significant digits (1 <= n_digits <= JS_DTOA_MAX_DIGITS) */
#define JS_DTOA_FORMAT_FIXED (1 << 0)
/* force fractional format: [-]dd.dd with n_digits fractional digits.
0 <= n_digits <= JS_DTOA_MAX_DIGITS */
#define JS_DTOA_FORMAT_FRAC (2 << 0)
#define JS_DTOA_FORMAT_MASK (3 << 0)
/* select exponential notation either in fixed or free format */
#define JS_DTOA_EXP_AUTO (0 << 2)
#define JS_DTOA_EXP_ENABLED (1 << 2)
#define JS_DTOA_EXP_DISABLED (2 << 2)
#define JS_DTOA_EXP_MASK (3 << 2)
#define JS_DTOA_MINUS_ZERO (1 << 4) /* show the minus sign for -0 */
/* only accepts integers (no dot, no exponent) */
#define JS_ATOD_INT_ONLY (1 << 0)
/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
#define JS_ATOD_ACCEPT_BIN_OCT (1 << 1)
/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
#define JS_ATOD_ACCEPT_LEGACY_OCTAL (1 << 2)
/* accept _ between digits as a digit separator */
#define JS_ATOD_ACCEPT_UNDERSCORES (1 << 3)
typedef struct {
uint64_t mem[37];
} JSDTOATempMem;
typedef struct {
uint64_t mem[27];
} JSATODTempMem;
/* return a maximum bound of the string length */
int js_dtoa_max_len(double d, int radix, int n_digits, int flags);
/* return the string length */
int js_dtoa(char *buf, double d, int radix, int n_digits, int flags,
JSDTOATempMem *tmp_mem);
double js_atod(const char *str, const char **pnext, int radix, int flags,
JSATODTempMem *tmp_mem);
#ifdef JS_DTOA_DUMP_STATS
void js_dtoa_dump_stats(void);
#endif
/* additional exported functions */
size_t u32toa(char *buf, uint32_t n);
size_t i32toa(char *buf, int32_t n);
size_t u64toa(char *buf, uint64_t n);
size_t i64toa(char *buf, int64_t n);
size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix);
size_t i64toa_radix(char *buf, int64_t n, unsigned int radix);

2260
lib/mquickjs/libm.c Normal file

File diff suppressed because it is too large Load Diff

46
lib/mquickjs/libm.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Tiny Math Library
*
* Copyright (c) 2024 Fabrice Bellard
*
* 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.
*/
double js_scalbn(double x, int n);
double js_floor(double x);
double js_ceil(double x);
double js_trunc(double x);
double js_round_inf(double a);
double js_fabs(double x);
double js_sqrt(double x);
int32_t js_lrint(double a);
double js_fmod(double x, double y);
double js_sin(double x);
double js_cos(double x);
double js_tan(double x);
double js_acos(double x);
double js_asin(double x);
double js_atan(double x);
double js_atan2(double y, double x);
double js_exp(double x);
double js_log(double x);
double js_log2(double x);
double js_log10(double x);
double js_pow(double x, double y);
/* exported only for tests */
int js_rem_pio2(double x, double *y);

99
lib/mquickjs/list.h Normal file
View File

@ -0,0 +1,99 @@
/*
* Linux klist like system
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* 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 LIST_H
#define LIST_H
#ifndef NULL
#include <stddef.h>
#endif
struct list_head {
struct list_head *prev;
struct list_head *next;
};
#define LIST_HEAD_INIT(el) { &(el), &(el) }
/* return the pointer of type 'type *' containing 'el' as field 'member' */
#define list_entry(el, type, member) container_of(el, type, member)
static inline void init_list_head(struct list_head *head)
{
head->prev = head;
head->next = head;
}
/* insert 'el' between 'prev' and 'next' */
static inline void __list_add(struct list_head *el,
struct list_head *prev, struct list_head *next)
{
prev->next = el;
el->prev = prev;
el->next = next;
next->prev = el;
}
/* add 'el' at the head of the list 'head' (= after element head) */
static inline void list_add(struct list_head *el, struct list_head *head)
{
__list_add(el, head, head->next);
}
/* add 'el' at the end of the list 'head' (= before element head) */
static inline void list_add_tail(struct list_head *el, struct list_head *head)
{
__list_add(el, head->prev, head);
}
static inline void list_del(struct list_head *el)
{
struct list_head *prev, *next;
prev = el->prev;
next = el->next;
prev->next = next;
next->prev = prev;
el->prev = NULL; /* fail safe */
el->next = NULL; /* fail safe */
}
static inline int list_empty(struct list_head *el)
{
return el->next == el;
}
#define list_for_each(el, head) \
for(el = (head)->next; el != (head); el = el->next)
#define list_for_each_safe(el, el1, head) \
for(el = (head)->next, el1 = el->next; el != (head); \
el = el1, el1 = el->next)
#define list_for_each_prev(el, head) \
for(el = (head)->prev; el != (head); el = el->prev)
#define list_for_each_prev_safe(el, el1, head) \
for(el = (head)->prev, el1 = el->prev; el != (head); \
el = el1, el1 = el->prev)
#endif /* LIST_H */

18324
lib/mquickjs/mquickjs.c Normal file

File diff suppressed because it is too large Load Diff

382
lib/mquickjs/mquickjs.h Normal file
View File

@ -0,0 +1,382 @@
/*
* Micro QuickJS Javascript Engine
*
* Copyright (c) 2017-2025 Fabrice Bellard
* Copyright (c) 2017-2025 Charlie Gordon
*
* 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 MQUICKJS_H
#define MQUICKJS_H
#include <inttypes.h>
#if defined(__GNUC__) || defined(__clang__)
#define __js_printf_like(f, a) __attribute__((format(printf, f, a)))
#else
#define __js_printf_like(a, b)
#endif
#if INTPTR_MAX >= INT64_MAX
#define JS_PTR64 /* pointers are 64 bit wide instead of 32 bit wide */
#endif
typedef struct JSContext JSContext;
#ifdef JS_PTR64
typedef uint64_t JSWord;
typedef uint64_t JSValue;
#define JSW 8
#define JSValue_PRI PRIo64
#define JS_USE_SHORT_FLOAT
#else
typedef uint32_t JSWord;
typedef uint32_t JSValue;
#define JSW 4
#define JSValue_PRI PRIo32
#endif
#define JS_BOOL int
enum {
JS_TAG_INT = 0, /* 31 bit integer (1 bit) */
JS_TAG_PTR = 1, /* pointer (2 bits) */
JS_TAG_SPECIAL = 3, /* other special values (2 bits) */
JS_TAG_BOOL = JS_TAG_SPECIAL | (0 << 2), /* (5 bits) */
JS_TAG_NULL = JS_TAG_SPECIAL | (1 << 2), /* (5 bits) */
JS_TAG_UNDEFINED = JS_TAG_SPECIAL | (2 << 2), /* (5 bits) */
JS_TAG_EXCEPTION = JS_TAG_SPECIAL | (3 << 2), /* (5 bits) */
JS_TAG_SHORT_FUNC = JS_TAG_SPECIAL | (4 << 2), /* (5 bits) */
JS_TAG_UNINITIALIZED = JS_TAG_SPECIAL | (5 << 2), /* (5 bits) */
JS_TAG_STRING_CHAR = JS_TAG_SPECIAL | (6 << 2), /* (5 bits) */
JS_TAG_CATCH_OFFSET = JS_TAG_SPECIAL | (7 << 2), /* (5 bits) */
#ifdef JS_USE_SHORT_FLOAT
JS_TAG_SHORT_FLOAT = 5, /* 3 bits */
#endif
};
#define JS_TAG_SPECIAL_BITS 5
#define JS_VALUE_GET_INT(v) ((int)(v) >> 1)
#define JS_VALUE_GET_SPECIAL_VALUE(v) ((int)(v) >> JS_TAG_SPECIAL_BITS)
#define JS_VALUE_GET_SPECIAL_TAG(v) ((v) & ((1 << JS_TAG_SPECIAL_BITS) - 1))
#define JS_VALUE_MAKE_SPECIAL(tag, v) ((tag) | ((v) << JS_TAG_SPECIAL_BITS))
#define JS_NULL JS_VALUE_MAKE_SPECIAL(JS_TAG_NULL, 0)
#define JS_UNDEFINED JS_VALUE_MAKE_SPECIAL(JS_TAG_UNDEFINED, 0)
#define JS_UNINITIALIZED JS_VALUE_MAKE_SPECIAL(JS_TAG_UNINITIALIZED, 0)
#define JS_FALSE JS_VALUE_MAKE_SPECIAL(JS_TAG_BOOL, 0)
#define JS_TRUE JS_VALUE_MAKE_SPECIAL(JS_TAG_BOOL, 1)
#define JS_EX_NORMAL 0 /* all exceptions except not enough memory */
#define JS_EX_CALL 1 /* specific exception to generate a tail call. The call flags are added */
#define JS_EXCEPTION JS_VALUE_MAKE_SPECIAL(JS_TAG_EXCEPTION, JS_EX_NORMAL)
typedef enum {
JS_CLASS_OBJECT,
JS_CLASS_ARRAY,
JS_CLASS_C_FUNCTION,
JS_CLASS_CLOSURE,
JS_CLASS_NUMBER,
JS_CLASS_BOOLEAN,
JS_CLASS_STRING,
JS_CLASS_DATE,
JS_CLASS_REGEXP,
JS_CLASS_ERROR,
JS_CLASS_EVAL_ERROR,
JS_CLASS_RANGE_ERROR,
JS_CLASS_REFERENCE_ERROR,
JS_CLASS_SYNTAX_ERROR,
JS_CLASS_TYPE_ERROR,
JS_CLASS_URI_ERROR,
JS_CLASS_INTERNAL_ERROR,
JS_CLASS_ARRAY_BUFFER,
JS_CLASS_TYPED_ARRAY,
JS_CLASS_UINT8C_ARRAY,
JS_CLASS_INT8_ARRAY,
JS_CLASS_UINT8_ARRAY,
JS_CLASS_INT16_ARRAY,
JS_CLASS_UINT16_ARRAY,
JS_CLASS_INT32_ARRAY,
JS_CLASS_UINT32_ARRAY,
JS_CLASS_FLOAT32_ARRAY,
JS_CLASS_FLOAT64_ARRAY,
JS_CLASS_USER, /* user classes start from this value */
} JSObjectClassEnum;
/* predefined functions */
typedef enum {
JS_CFUNCTION_bound,
JS_CFUNCTION_USER, /* user functions start from this value */
} JSCFunctionEnum;
/* temporary buffer to hold C strings */
typedef struct {
uint8_t buf[5];
} JSCStringBuf;
typedef struct JSGCRef {
JSValue val;
struct JSGCRef *prev;
} JSGCRef;
/* stack of JSGCRef */
JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref);
JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref);
#define JS_PUSH_VALUE(ctx, v) do { JS_PushGCRef(ctx, &v ## _ref); v ## _ref.val = v; } while (0)
#define JS_POP_VALUE(ctx, v) v = JS_PopGCRef(ctx, &v ## _ref)
/* list of JSGCRef (they can be removed in any order, slower) */
JSValue *JS_AddGCRef(JSContext *ctx, JSGCRef *ref);
void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref);
JSValue JS_NewFloat64(JSContext *ctx, double d);
JSValue JS_NewInt32(JSContext *ctx, int32_t val);
JSValue JS_NewUint32(JSContext *ctx, uint32_t val);
JSValue JS_NewInt64(JSContext *ctx, int64_t val);
static inline JS_BOOL JS_IsInt(JSValue v)
{
return (v & 1) == JS_TAG_INT;
}
static inline JS_BOOL JS_IsPtr(JSValue v)
{
return (v & (JSW - 1)) == JS_TAG_PTR;
}
#ifdef JS_USE_SHORT_FLOAT
static inline JS_BOOL JS_IsShortFloat(JSValue v)
{
return (v & (JSW - 1)) == JS_TAG_SHORT_FLOAT;
}
#endif
static inline JS_BOOL JS_IsBool(JSValue v)
{
return JS_VALUE_GET_SPECIAL_TAG(v) == JS_TAG_BOOL;
}
static inline JS_BOOL JS_IsNull(JSValue v)
{
return v == JS_NULL;
}
static inline JS_BOOL JS_IsUndefined(JSValue v)
{
return v == JS_UNDEFINED;
}
static inline JS_BOOL JS_IsUninitialized(JSValue v)
{
return v == JS_UNINITIALIZED;
}
static inline JS_BOOL JS_IsException(JSValue v)
{
return v == JS_EXCEPTION;
}
static inline JSValue JS_NewBool(int val)
{
return JS_VALUE_MAKE_SPECIAL(JS_TAG_BOOL, (val != 0));
}
JS_BOOL JS_IsNumber(JSContext *ctx, JSValue val);
JS_BOOL JS_IsString(JSContext *ctx, JSValue val);
JS_BOOL JS_IsError(JSContext *ctx, JSValue val);
JS_BOOL JS_IsFunction(JSContext *ctx, JSValue val);
int JS_GetClassID(JSContext *ctx, JSValue val);
void JS_SetOpaque(JSContext *ctx, JSValue val, void *opaque);
void *JS_GetOpaque(JSContext *ctx, JSValue val);
typedef JSValue JSCFunction(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv);
/* no JS function call be called from a C finalizer */
typedef void (*JSCFinalizer)(JSContext *ctx, void *opaque);
typedef enum JSCFunctionDefEnum { /* XXX: should rename for namespace isolation */
JS_CFUNC_generic,
JS_CFUNC_generic_magic,
JS_CFUNC_constructor,
JS_CFUNC_constructor_magic,
JS_CFUNC_generic_params,
JS_CFUNC_f_f,
} JSCFunctionDefEnum;
typedef union JSCFunctionType {
JSCFunction *generic;
JSValue (*generic_magic)(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv, int magic);
JSCFunction *constructor;
JSValue (*constructor_magic)(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv, int magic);
JSValue (*generic_params)(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv, JSValue params);
double (*f_f)(double f);
} JSCFunctionType;
typedef struct JSCFunctionDef {
JSCFunctionType func;
JSValue name;
uint8_t def_type;
uint8_t arg_count;
int16_t magic;
} JSCFunctionDef;
typedef struct {
const JSWord *stdlib_table;
const JSCFunctionDef *c_function_table;
const JSCFinalizer *c_finalizer_table;
uint32_t stdlib_table_len;
uint32_t stdlib_table_align;
uint32_t sorted_atoms_offset;
uint32_t global_object_offset;
uint32_t class_count;
} JSSTDLibraryDef;
typedef void JSWriteFunc(void *opaque, const void *buf, size_t buf_len);
/* return != 0 if the JS code needs to be interrupted */
typedef int JSInterruptHandler(JSContext *ctx, void *opaque);
JSContext *JS_NewContext(void *mem_start, size_t mem_size, const JSSTDLibraryDef *stdlib_def);
/* if prepare_compilation is true, the context will be used to compile
to a binary file. JS_NewContext2() is not expected to be used in
the embedded version */
JSContext *JS_NewContext2(void *mem_start, size_t mem_size, const JSSTDLibraryDef *stdlib_def, JS_BOOL prepare_compilation);
void JS_FreeContext(JSContext *ctx);
void JS_SetContextOpaque(JSContext *ctx, void *opaque);
void JS_SetInterruptHandler(JSContext *ctx, JSInterruptHandler *interrupt_handler);
void JS_SetRandomSeed(JSContext *ctx, uint64_t seed);
JSValue JS_GetGlobalObject(JSContext *ctx);
JSValue JS_Throw(JSContext *ctx, JSValue obj);
JSValue __js_printf_like(3, 4) JS_ThrowError(JSContext *ctx, JSObjectClassEnum error_num,
const char *fmt, ...);
#define JS_ThrowTypeError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_TYPE_ERROR, fmt, ##__VA_ARGS__)
#define JS_ThrowReferenceError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_REFERENCE_ERROR, fmt, ##__VA_ARGS__)
#define JS_ThrowInternalError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_INTERNAL_ERROR, fmt, ##__VA_ARGS__)
#define JS_ThrowRangeError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_RANGE_ERROR, fmt, ##__VA_ARGS__)
#define JS_ThrowSyntaxError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_SYNTAX_ERROR, fmt, ##__VA_ARGS__)
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
JSValue JS_GetPropertyStr(JSContext *ctx, JSValue this_obj, const char *str);
JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue obj, uint32_t idx);
JSValue JS_SetPropertyStr(JSContext *ctx, JSValue this_obj,
const char *str, JSValue val);
JSValue JS_SetPropertyUint32(JSContext *ctx, JSValue this_obj,
uint32_t idx, JSValue val);
JSValue JS_NewObjectClassUser(JSContext *ctx, int class_id);
JSValue JS_NewObject(JSContext *ctx);
JSValue JS_NewArray(JSContext *ctx, int initial_len);
/* create a C function with an object parameter (closure) */
JSValue JS_NewCFunctionParams(JSContext *ctx, int func_idx, JSValue params);
#define JS_EVAL_RETVAL (1 << 0) /* return the last value instead of undefined (slower code) */
#define JS_EVAL_REPL (1 << 1) /* implicitly defined global variables in assignments */
#define JS_EVAL_STRIP_COL (1 << 2) /* strip column number debug information (save memory) */
#define JS_EVAL_JSON (1 << 3) /* parse as JSON and return the object */
#define JS_EVAL_REGEXP (1 << 4) /* internal use */
#define JS_EVAL_REGEXP_FLAGS_SHIFT 8 /* internal use */
JSValue JS_Parse(JSContext *ctx, const char *input, size_t input_len,
const char *filename, int eval_flags);
JSValue JS_Run(JSContext *ctx, JSValue val);
JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
const char *filename, int eval_flags);
void JS_GC(JSContext *ctx);
JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len);
JSValue JS_NewString(JSContext *ctx, const char *buf);
const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValue val, JSCStringBuf *buf);
const char *JS_ToCString(JSContext *ctx, JSValue val, JSCStringBuf *buf);
JSValue JS_ToString(JSContext *ctx, JSValue val);
int JS_ToInt32(JSContext *ctx, int *pres, JSValue val);
int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValue val);
int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValue val);
int JS_ToNumber(JSContext *ctx, double *pres, JSValue val);
JSValue JS_GetException(JSContext *ctx);
int JS_StackCheck(JSContext *ctx, uint32_t len);
void JS_PushArg(JSContext *ctx, JSValue val);
#define FRAME_CF_CTOR (1 << 16) /* also ored with argc in
C constructors */
JSValue JS_Call(JSContext *ctx, int call_flags);
#define JS_BYTECODE_MAGIC 0xacfb
typedef struct {
uint16_t magic; /* JS_BYTECODE_MAGIC */
uint16_t version;
uintptr_t base_addr;
JSValue unique_strings;
JSValue main_func;
} JSBytecodeHeader;
/* only used on the host when compiling to file */
void JS_PrepareBytecode(JSContext *ctx,
JSBytecodeHeader *hdr,
const uint8_t **pdata_buf, uint32_t *pdata_len,
JSValue eval_code);
/* only used on the host when compiling to file */
int JS_RelocateBytecode2(JSContext *ctx, JSBytecodeHeader *hdr,
uint8_t *buf, uint32_t buf_len,
uintptr_t new_base_addr, JS_BOOL update_atoms);
#if JSW == 8
typedef struct {
uint16_t magic; /* JS_BYTECODE_MAGIC */
uint16_t version;
uint32_t base_addr;
uint32_t unique_strings;
uint32_t main_func;
} JSBytecodeHeader32;
/* only used on the host when compiling to file. A 32 bit bytecode is generated on a 64 bit host. */
int JS_PrepareBytecode64to32(JSContext *ctx,
JSBytecodeHeader32 *hdr,
const uint8_t **pdata_buf, uint32_t *pdata_len,
JSValue eval_code);
#endif
JS_BOOL JS_IsBytecode(const uint8_t *buf, size_t buf_len);
/* Relocate the bytecode in 'buf' so that it can be executed
later. Return 0 if OK, != 0 if error */
int JS_RelocateBytecode(JSContext *ctx,
uint8_t *buf, uint32_t buf_len);
/* Load the precompiled bytecode from 'buf'. 'buf' must be allocated
as long as the JSContext exists. Use JS_Run() to execute
it. warning: the bytecode is not checked so it should come from a
trusted source. */
JSValue JS_LoadBytecode(JSContext *ctx, const uint8_t *buf);
/* debug functions */
void JS_SetLogFunc(JSContext *ctx, JSWriteFunc *write_func);
void JS_PrintValue(JSContext *ctx, JSValue val);
#define JS_DUMP_LONG (1 << 0) /* display object/array content */
#define JS_DUMP_NOQUOTE (1 << 1) /* strings: no quote for identifiers */
/* for low level dumps: don't dump special properties and use specific
quotes to distinguish string chars, unique strings and normal
strings */
#define JS_DUMP_RAW (1 << 2)
void JS_PrintValueF(JSContext *ctx, JSValue val, int flags);
void JS_DumpValueF(JSContext *ctx, const char *str,
JSValue val, int flags);
void JS_DumpValue(JSContext *ctx, const char *str,
JSValue val);
void JS_DumpMemory(JSContext *ctx, JS_BOOL is_long);
#endif /* MQUICKJS_H */

View File

@ -0,0 +1,76 @@
#define JS_ATOM_null 0
#define JS_ATOM_false 3
#define JS_ATOM_true 6
#define JS_ATOM_if 9
#define JS_ATOM_else 11
#define JS_ATOM_return 14
#define JS_ATOM_var 17
#define JS_ATOM_this 19
#define JS_ATOM_delete 22
#define JS_ATOM_void 25
#define JS_ATOM_typeof 28
#define JS_ATOM_new 31
#define JS_ATOM_in 33
#define JS_ATOM_instanceof 35
#define JS_ATOM_do 39
#define JS_ATOM_while 41
#define JS_ATOM_for 44
#define JS_ATOM_break 46
#define JS_ATOM_continue 49
#define JS_ATOM_switch 53
#define JS_ATOM_case 56
#define JS_ATOM_default 59
#define JS_ATOM_throw 62
#define JS_ATOM_try 65
#define JS_ATOM_catch 67
#define JS_ATOM_finally 70
#define JS_ATOM_function 73
#define JS_ATOM_debugger 77
#define JS_ATOM_with 81
#define JS_ATOM_class 84
#define JS_ATOM_const 87
#define JS_ATOM_enum 90
#define JS_ATOM_export 93
#define JS_ATOM_extends 96
#define JS_ATOM_import 99
#define JS_ATOM_super 102
#define JS_ATOM_implements 105
#define JS_ATOM_interface 109
#define JS_ATOM_let 113
#define JS_ATOM_package 115
#define JS_ATOM_private 118
#define JS_ATOM_protected 121
#define JS_ATOM_public 125
#define JS_ATOM_static 128
#define JS_ATOM_yield 131
#define JS_ATOM_empty 134
#define JS_ATOM_toString 136
#define JS_ATOM_valueOf 140
#define JS_ATOM_number 143
#define JS_ATOM_object 146
#define JS_ATOM_undefined 149
#define JS_ATOM_string 153
#define JS_ATOM_boolean 156
#define JS_ATOM__ret_ 159
#define JS_ATOM__eval_ 162
#define JS_ATOM_eval 165
#define JS_ATOM_arguments 168
#define JS_ATOM_value 172
#define JS_ATOM_get 175
#define JS_ATOM_set 177
#define JS_ATOM_prototype 179
#define JS_ATOM_constructor 183
#define JS_ATOM_length 187
#define JS_ATOM_target 190
#define JS_ATOM_of 193
#define JS_ATOM_NaN 195
#define JS_ATOM_Infinity 197
#define JS_ATOM__Infinity 201
#define JS_ATOM_name 205
#define JS_ATOM_Error 208
#define JS_ATOM___proto__ 211
#define JS_ATOM_index 215
#define JS_ATOM_input 218
#define JS_ATOM_END 221

View File

@ -0,0 +1,932 @@
/*
* Micro QuickJS build utility
*
* Copyright (c) 2017-2025 Fabrice Bellard
* Copyright (c) 2017-2025 Charlie Gordon
*
* 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 <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include "cutils.h"
#include "list.h"
#include "mquickjs_build.h"
static unsigned JSW = 4; // override this with -m64
typedef struct {
char *str;
int offset;
} AtomDef;
typedef struct {
AtomDef *tab;
int count;
int size;
int offset;
} AtomList;
typedef struct {
char *name;
int length;
char *magic;
char *cproto_name;
char *cfunc_name;
} CFuncDef;
typedef struct {
CFuncDef *tab;
int count;
int size;
} CFuncList;
typedef struct {
struct list_head link;
const JSClassDef *class1;
int class_idx;
char *finalizer_name;
char *class_id;
} ClassDefEntry;
typedef struct {
AtomList atom_list;
CFuncList cfunc_list;
int cur_offset;
int sorted_atom_table_offset;
int global_object_offset;
struct list_head class_list;
} BuildContext;
static const char *atoms[] = {
#define DEF(a, b) b,
/* keywords */
DEF(null, "null") /* must be first */
DEF(false, "false")
DEF(true, "true")
DEF(if, "if")
DEF(else, "else")
DEF(return, "return")
DEF(var, "var")
DEF(this, "this")
DEF(delete, "delete")
DEF(void, "void")
DEF(typeof, "typeof")
DEF(new, "new")
DEF(in, "in")
DEF(instanceof, "instanceof")
DEF(do, "do")
DEF(while, "while")
DEF(for, "for")
DEF(break, "break")
DEF(continue, "continue")
DEF(switch, "switch")
DEF(case, "case")
DEF(default, "default")
DEF(throw, "throw")
DEF(try, "try")
DEF(catch, "catch")
DEF(finally, "finally")
DEF(function, "function")
DEF(debugger, "debugger")
DEF(with, "with")
/* FutureReservedWord */
DEF(class, "class")
DEF(const, "const")
DEF(enum, "enum")
DEF(export, "export")
DEF(extends, "extends")
DEF(import, "import")
DEF(super, "super")
/* FutureReservedWords when parsing strict mode code */
DEF(implements, "implements")
DEF(interface, "interface")
DEF(let, "let")
DEF(package, "package")
DEF(private, "private")
DEF(protected, "protected")
DEF(public, "public")
DEF(static, "static")
DEF(yield, "yield")
#undef DEF
/* other atoms */
"",
"toString",
"valueOf",
"number",
"object",
"undefined",
"string",
"boolean",
"<ret>",
"<eval>",
"eval",
"arguments",
"value",
"get",
"set",
"prototype",
"constructor",
"length",
"target",
"of",
"NaN",
"Infinity",
"-Infinity",
"name",
"Error",
"__proto__",
"index",
"input",
};
static char *cvt_name(char *buf, size_t buf_size, const char *str)
{
size_t i, len = strlen(str);
assert(len < buf_size);
if (len == 0) {
strcpy(buf, "empty");
} else {
strcpy(buf, str);
for(i = 0; i < len; i++) {
if (buf[i] == '<' || buf[i] == '>' || buf[i] == '-')
buf[i] = '_';
}
}
return buf;
}
static BOOL is_ascii_string(const char *buf, size_t len)
{
size_t i;
for(i = 0; i < len; i++) {
if ((uint8_t)buf[i] > 0x7f)
return FALSE;
}
return TRUE;
}
static BOOL is_numeric_string(const char *buf, size_t len)
{
return (!strcmp(buf, "NaN") ||
!strcmp(buf, "Infinity") ||
!strcmp(buf, "-Infinity"));
}
static int find_atom(AtomList *s, const char *str)
{
int i;
for(i = 0; i < s->count; i++) {
if (!strcmp(str, s->tab[i].str))
return i;
}
return -1;
}
static int add_atom(AtomList *s, const char *str)
{
int i;
AtomDef *e;
i = find_atom(s, str);
if (i >= 0)
return s->tab[i].offset;
if ((s->count + 1) > s->size) {
s->size = max_int(s->count + 1, s->size * 3 / 2);
s->tab = realloc(s->tab, sizeof(s->tab[0]) * s->size);
}
e = &s->tab[s->count++];
e->str = strdup(str);
e->offset = s->offset;
s->offset += 1 + ((strlen(str) + JSW) / JSW);
return s->count - 1;
}
static int add_cfunc(CFuncList *s, const char *name, int length, const char *magic, const char *cproto_name, const char *cfunc_name)
{
int i;
CFuncDef *e;
for(i = 0; i < s->count; i++) {
e = &s->tab[i];
if (!strcmp(name, e->name) &&
length == e->length &&
!strcmp(magic, e->magic) &&
!strcmp(cproto_name, e->cproto_name) &&
!strcmp(cfunc_name, e->cfunc_name)) {
return i;
}
}
if ((s->count + 1) > s->size) {
s->size = max_int(s->count + 1, s->size * 3 / 2);
s->tab = realloc(s->tab, sizeof(s->tab[0]) * s->size);
}
e = &s->tab[s->count++];
e->name = strdup(name);
e->magic = strdup(magic);
e->length = length;
e->cproto_name = strdup(cproto_name);
e->cfunc_name = strdup(cfunc_name);
return s->count - 1;
}
static void dump_atom_defines(void)
{
AtomList atom_list_s, *s = &atom_list_s;
AtomDef *e;
int i;
char buf[256];
memset(s, 0, sizeof(*s));
/* add the predefined atoms (they have a corresponding define) */
for(i = 0; i < countof(atoms); i++) {
add_atom(s, atoms[i]);
}
for(i = 0; i < s->count; i++) {
e = &s->tab[i];
printf("#define JS_ATOM_%s %d\n",
cvt_name(buf, sizeof(buf), e->str), e->offset);
}
printf("\n");
printf("#define JS_ATOM_END %d\n", s->offset);
printf("\n");
}
static int atom_cmp(const void *p1, const void *p2)
{
const AtomDef *a1 = (const AtomDef *)p1;
const AtomDef *a2 = (const AtomDef *)p2;
return strcmp(a1->str, a2->str);
}
/* js_atom_table must be properly aligned because the property hash
table uses the low bits of the atom pointer value */
#define ATOM_ALIGN 64
static void dump_atoms(BuildContext *ctx)
{
AtomList *s = &ctx->atom_list;
int i, j, k, l, len, len1, is_ascii, is_numeric;
uint64_t v;
const char *str;
AtomDef *sorted_atoms;
char buf[256];
sorted_atoms = malloc(sizeof(sorted_atoms[0]) * s->count);
memcpy(sorted_atoms, s->tab, sizeof(sorted_atoms[0]) * s->count);
qsort(sorted_atoms, s->count, sizeof(sorted_atoms[0]), atom_cmp);
printf(" /* atom_table */\n");
for(i = 0; i < s->count; i++) {
str = s->tab[i].str;
len = strlen(str);
is_ascii = is_ascii_string(str, len);
is_numeric = is_numeric_string(str, len);
printf(" (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (%d << (JS_MTAG_BITS + 1)) | (%d << (JS_MTAG_BITS + 2)) | (%d << (JS_MTAG_BITS + 3)), /* \"%s\" (offset=%d) */\n",
is_ascii, is_numeric, len, str, ctx->cur_offset);
len1 = (len + JSW) / JSW;
for(j = 0; j < len1; j++) {
l = min_uint32(JSW, len - j * JSW);
v = 0;
for(k = 0; k < l; k++)
v |= (uint64_t)(uint8_t)str[j * JSW + k] << (k * 8);
printf(" 0x%0*" PRIx64 ",\n", JSW * 2, v);
}
assert(ctx->cur_offset == s->tab[i].offset);
ctx->cur_offset += len1 + 1;
}
printf("\n");
ctx->sorted_atom_table_offset = ctx->cur_offset;
printf(" /* sorted atom table (offset=%d) */\n", ctx->cur_offset);
printf(" JS_VALUE_ARRAY_HEADER(%d),\n", s->count);
for(i = 0; i < s->count; i++) {
AtomDef *e = &sorted_atoms[i];
printf(" JS_ROM_VALUE(%d), /* %s */\n",
e->offset, cvt_name(buf, sizeof(buf), e->str));
}
ctx->cur_offset += s->count + 1;
printf("\n");
free(sorted_atoms);
}
static int define_value(BuildContext *s, const JSPropDef *d);
static uint32_t dump_atom(BuildContext *s, const char *str, BOOL value_only)
{
int len, idx, i, offset;
len = strlen(str);
for(i = 0; i < len; i++) {
if ((uint8_t)str[i] >= 128) {
fprintf(stderr, "unicode property names are not supported yet (%s)\n", str);
exit(1);
}
}
if (len >= 1 && (str[0] >= '0' && str[0] <= '9')) {
fprintf(stderr, "numeric property names are not supported yet (%s)\n", str);
exit(1);
}
if (len == 1) {
if (value_only) {
/* XXX: hardcoded */
return ((uint8_t)str[0] << 5) | 0x1b;
}
printf("JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, %d)",
(uint8_t)str[0]);
} else {
idx = find_atom(&s->atom_list, str);
if (idx < 0) {
fprintf(stderr, "atom '%s' is undefined\n", str);
exit(1);
}
offset = s->atom_list.tab[idx].offset;
if (value_only)
return (offset * JSW) + 1; /* correct modulo ATOM_ALIGN */
printf("JS_ROM_VALUE(%d)", offset);
}
printf(" /* %s */", str);
return 0;
}
static void dump_cfuncs(BuildContext *s)
{
int i;
CFuncDef *e;
printf("static const JSCFunctionDef js_c_function_table[] = {\n");
for(i = 0; i < s->cfunc_list.count; i++) {
e = &s->cfunc_list.tab[i];
printf(" { { .%s = %s },\n", e->cproto_name, e->cfunc_name);
printf(" ");
dump_atom(s, e->name, FALSE);
printf(",\n");
printf(" JS_CFUNC_%s, %d, %s },\n",
e->cproto_name, e->length, e->magic);
}
printf("};\n\n");
}
static void dump_cfinalizers(BuildContext *s)
{
struct list_head *el;
ClassDefEntry *e;
printf("static const JSCFinalizer js_c_finalizer_table[JS_CLASS_COUNT - JS_CLASS_USER] = {\n");
list_for_each(el, &s->class_list) {
e = list_entry(el, ClassDefEntry, link);
if (e->finalizer_name &&
strcmp(e->finalizer_name, "NULL") != 0) {
printf(" [%s - JS_CLASS_USER] = %s,\n", e->class_id, e->finalizer_name);
}
}
printf("};\n\n");
}
typedef enum {
PROPS_KIND_GLOBAL,
PROPS_KIND_PROTO,
PROPS_KIND_CLASS,
PROPS_KIND_OBJECT,
} JSPropsKindEnum;
static inline uint32_t hash_prop(BuildContext *s, const char *name)
{
/* Compute the hash for a symbol, must be consistent with
mquickjs.c implementation.
*/
uint32_t prop = dump_atom(s, name, TRUE);
return (prop / JSW) ^ (prop % JSW); /* XXX: improve */
}
static int define_props(BuildContext *s, const JSPropDef *props_def,
JSPropsKindEnum props_kind, const char *class_id_str)
{
int i, *ident_tab, idx, props_ident, n_props;
int prop_idx;
const JSPropDef *d;
uint32_t *prop_hash;
BOOL is_global_object = (props_kind == PROPS_KIND_GLOBAL);
static const JSPropDef dummy_props[] = {
{ JS_DEF_END },
};
if (!props_def)
props_def = dummy_props;
n_props = 0;
for(d = props_def; d->def_type != JS_DEF_END; d++) {
n_props++;
}
if (props_kind == PROPS_KIND_PROTO ||
props_kind == PROPS_KIND_CLASS)
n_props++;
ident_tab = malloc(sizeof(ident_tab[0]) * n_props);
/* define the various objects */
for(d = props_def, i = 0; d->def_type != JS_DEF_END; d++, i++) {
ident_tab[i] = define_value(s, d);
}
props_ident = -1;
prop_hash = NULL;
if (is_global_object) {
props_ident = s->cur_offset;
printf(" /* global object properties (offset=%d) */\n", props_ident);
printf(" JS_VALUE_ARRAY_HEADER(%d),\n", 2 * n_props);
s->cur_offset += 2 * n_props + 1;
} else {
int hash_size_log2;
uint32_t hash_size, hash_mask;
uint32_t *hash_table, h;
if (n_props <= 1)
hash_size_log2 = 0;
else
hash_size_log2 = (32 - clz32(n_props - 1)) - 1;
hash_size = 1 << hash_size_log2;
if (hash_size > ATOM_ALIGN / JSW) {
#if !defined __APPLE__
// XXX: Cannot request data alignment larger than 64 bytes on Darwin
fprintf(stderr, "Too many properties, consider increasing ATOM_ALIGN\n");
#endif
hash_size = ATOM_ALIGN / JSW;
}
hash_mask = hash_size - 1;
hash_table = malloc(sizeof(hash_table[0]) * hash_size);
prop_hash = malloc(sizeof(prop_hash[0]) * n_props);
/* build the hash table */
for(i = 0; i < hash_size; i++)
hash_table[i] = 0;
prop_idx = 0;
for(i = 0, d = props_def; i < n_props; i++, d++) {
const char *name;
if (d->def_type != JS_DEF_END) {
name = d->name;
} else {
if (props_kind == PROPS_KIND_PROTO)
name = "constructor";
else
name = "prototype";
}
h = hash_prop(s, name) & hash_mask;
prop_hash[prop_idx] = hash_table[h];
hash_table[h] = 2 + hash_size + 3 * prop_idx;
prop_idx++;
}
props_ident = s->cur_offset;
printf(" /* properties (offset=%d) */\n", props_ident);
printf(" JS_VALUE_ARRAY_HEADER(%d),\n", 2 + hash_size + n_props * 3);
printf(" %d << 1, /* n_props */\n", n_props);
printf(" %d << 1, /* hash_mask */\n", hash_mask);
for(i = 0; i < hash_size; i++) {
printf(" %d << 1,\n", hash_table[i]);
}
s->cur_offset += hash_size + 3 + 3 * n_props;
free(hash_table);
}
prop_idx = 0;
for(d = props_def, i = 0; i < n_props; d++, i++) {
const char *name, *prop_type;
/* name */
printf(" ");
if (d->def_type != JS_DEF_END) {
name = d->name;
} else {
if (props_kind == PROPS_KIND_PROTO)
name = "constructor";
else
name = "prototype";
}
dump_atom(s, name, FALSE);
printf(",\n");
printf(" ");
prop_type = "NORMAL";
switch(d->def_type) {
case JS_DEF_PROP_DOUBLE:
if (ident_tab[i] >= 0)
goto value_ptr;
/* short int */
printf("%d << 1,", (int32_t)d->u.f64);
break;
case JS_DEF_CGETSET:
if (is_global_object) {
fprintf(stderr, "getter/setter forbidden in global object\n");
exit(1);
}
prop_type = "GETSET";
goto value_ptr;
case JS_DEF_CLASS:
value_ptr:
assert(ident_tab[i] >= 0);
printf("JS_ROM_VALUE(%d),", ident_tab[i]);
break;
case JS_DEF_PROP_UNDEFINED:
printf("JS_UNDEFINED,");
break;
case JS_DEF_PROP_NULL:
printf("JS_NULL,");
break;
case JS_DEF_PROP_STRING:
dump_atom(s, d->u.str, FALSE);
printf(",");
break;
case JS_DEF_CFUNC:
idx = add_cfunc(&s->cfunc_list,
d->name,
d->u.func.length,
d->u.func.magic,
d->u.func.cproto_name,
d->u.func.func_name);
printf("JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),", idx);
break;
case JS_DEF_END:
if (props_kind == PROPS_KIND_PROTO) {
/* constructor property */
printf("(uint32_t)(-%s - 1) << 1,", class_id_str);
} else {
/* prototype property */
printf("%s << 1,", class_id_str);
}
prop_type = "SPECIAL";
break;
default:
abort();
}
printf("\n");
if (!is_global_object) {
printf(" (%d << 1) | (JS_PROP_%s << 30),\n",
prop_hash[prop_idx], prop_type);
}
prop_idx++;
}
free(prop_hash);
free(ident_tab);
return props_ident;
}
static ClassDefEntry *find_class(BuildContext *s, const JSClassDef *d)
{
struct list_head *el;
ClassDefEntry *e;
list_for_each(el, &s->class_list) {
e = list_entry(el, ClassDefEntry, link);
if (e->class1 == d)
return e;
}
return NULL;
}
static void free_class_entries(BuildContext *s)
{
struct list_head *el, *el1;
ClassDefEntry *e;
list_for_each_safe(el, el1, &s->class_list) {
e = list_entry(el, ClassDefEntry, link);
free(e->class_id);
free(e->finalizer_name);
free(e);
}
init_list_head(&s->class_list);
}
static int define_class(BuildContext *s, const JSClassDef *d)
{
int ctor_func_idx = -1, class_props_idx = -1, proto_props_idx = -1;
int ident, parent_class_idx = -1;
ClassDefEntry *e;
/* check if the class is already defined */
e = find_class(s, d);
if (e)
return e->class_idx;
if (d->parent_class)
parent_class_idx = define_class(s, d->parent_class);
if (d->func_name) {
ctor_func_idx = add_cfunc(&s->cfunc_list,
d->name,
d->length,
d->class_id,
d->cproto_name,
d->func_name);
}
if (ctor_func_idx >= 0) {
class_props_idx = define_props(s, d->class_props, PROPS_KIND_CLASS, d->class_id);
proto_props_idx = define_props(s, d->proto_props, PROPS_KIND_PROTO, d->class_id);
} else {
if (d->class_props)
class_props_idx = define_props(s, d->class_props, PROPS_KIND_OBJECT, d->class_id);
}
ident = s->cur_offset;
printf(" /* class (offset=%d) */\n", ident);
printf(" JS_MB_HEADER_DEF(JS_MTAG_OBJECT),\n");
if (class_props_idx >= 0)
printf(" JS_ROM_VALUE(%d),\n", class_props_idx);
else
printf(" JS_NULL,\n");
printf(" %d,\n", ctor_func_idx);
if (proto_props_idx >= 0)
printf(" JS_ROM_VALUE(%d),\n", proto_props_idx);
else
printf(" JS_NULL,\n");
if (parent_class_idx >= 0) {
printf(" JS_ROM_VALUE(%d),\n", parent_class_idx);
} else {
printf(" JS_NULL,\n");
}
printf("\n");
s->cur_offset += 5;
e = malloc(sizeof(*e));
memset(e, 0, sizeof(*e));
e->class_idx = ident;
e->class1 = d;
if (ctor_func_idx >= 0) {
e->class_id = strdup(d->class_id);
e->finalizer_name = strdup(d->finalizer_name);
}
list_add_tail(&e->link, &s->class_list);
return ident;
}
#define JS_SHORTINT_MIN (-(1 << 30))
#define JS_SHORTINT_MAX ((1 << 30) - 1)
static BOOL is_short_int(double d)
{
return (d >= JS_SHORTINT_MIN && d <= JS_SHORTINT_MAX && (int32_t)d == d);
}
static int define_value(BuildContext *s, const JSPropDef *d)
{
int ident;
ident = -1;
switch(d->def_type) {
case JS_DEF_PROP_DOUBLE:
{
uint64_t v;
if (!is_short_int(d->u.f64)) {
ident = s->cur_offset;
printf(" /* float64 (offset=%d) */\n", ident);
printf(" JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),\n");
v = float64_as_uint64(d->u.f64);
if (JSW == 8) {
printf(" 0x%016zx,\n", (size_t)v);
printf("\n");
s->cur_offset += 2;
} else {
/* XXX: little endian assumed */
printf(" 0x%08x,\n", (uint32_t)v);
printf(" 0x%08x,\n", (uint32_t)(v >> 32));
printf("\n");
s->cur_offset += 3;
}
}
}
break;
case JS_DEF_CLASS:
ident = define_class(s, d->u.class1);
break;
case JS_DEF_CGETSET:
{
int get_idx = -1, set_idx = -1;
char buf[256];
if (strcmp(d->u.getset.get_func_name, "NULL") != 0) {
snprintf(buf, sizeof(buf), "get %s", d->name);
get_idx = add_cfunc(&s->cfunc_list,
buf,
0, /* length */
d->u.getset.magic,
d->u.getset.cproto_name,
d->u.getset.get_func_name);
}
if (strcmp(d->u.getset.set_func_name, "NULL") != 0) {
snprintf(buf, sizeof(buf), "set %s", d->name);
set_idx = add_cfunc(&s->cfunc_list,
buf,
1, /* length */
d->u.getset.magic,
d->u.getset.cproto_name,
d->u.getset.set_func_name);
}
ident = s->cur_offset;
printf(" /* getset (offset=%d) */\n", ident);
printf(" JS_VALUE_ARRAY_HEADER(2),\n");
if (get_idx >= 0)
printf(" JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),\n", get_idx);
else
printf(" JS_UNDEFINED,\n");
if (set_idx >= 0)
printf(" JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),\n", set_idx);
else
printf(" JS_UNDEFINED,\n");
printf("\n");
s->cur_offset += 3;
}
break;
default:
break;
}
return ident;
}
static void define_atoms_props(BuildContext *s, const JSPropDef *props_def, JSPropsKindEnum props_kind);
static void define_atoms_class(BuildContext *s, const JSClassDef *d)
{
ClassDefEntry *e;
/* check if the class is already defined */
e = find_class(s, d);
if (e)
return;
if (d->parent_class)
define_atoms_class(s, d->parent_class);
if (d->func_name)
add_atom(&s->atom_list, d->name);
if (d->class_props)
define_atoms_props(s, d->class_props, d->func_name ? PROPS_KIND_CLASS : PROPS_KIND_OBJECT);
if (d->proto_props)
define_atoms_props(s, d->proto_props, PROPS_KIND_PROTO);
}
static void define_atoms_props(BuildContext *s, const JSPropDef *props_def, JSPropsKindEnum props_kind)
{
const JSPropDef *d;
for(d = props_def; d->def_type != JS_DEF_END; d++) {
add_atom(&s->atom_list, d->name);
switch(d->def_type) {
case JS_DEF_PROP_STRING:
add_atom(&s->atom_list, d->u.str);
break;
case JS_DEF_CLASS:
define_atoms_class(s, d->u.class1);
break;
case JS_DEF_CGETSET:
{
char buf[256];
if (strcmp(d->u.getset.get_func_name, "NULL") != 0) {
snprintf(buf, sizeof(buf), "get %s", d->name);
add_atom(&s->atom_list, buf);
}
if (strcmp(d->u.getset.set_func_name, "NULL") != 0) {
snprintf(buf, sizeof(buf), "set %s", d->name);
add_atom(&s->atom_list, buf);
}
}
break;
default:
break;
}
}
}
static int usage(const char *name)
{
fprintf(stderr, "usage: %s {-m32 | -m64} [-a]\n", name);
fprintf(stderr,
" create a ROM file for the mquickjs standard library\n"
"--help list options\n"
"-m32 force generation for a 32 bit target\n"
"-m64 force generation for a 64 bit target\n"
"-a generate the mquickjs_atom.h header\n"
);
return 1;
}
int build_atoms(const char *stdlib_name, const JSPropDef *global_obj,
const JSPropDef *c_function_decl, int argc, char **argv)
{
int i;
unsigned jsw;
BuildContext ss, *s = &ss;
BOOL build_atom_defines = FALSE;
#if INTPTR_MAX >= INT64_MAX
jsw = 8;
#else
jsw = 4;
#endif
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-m64")) {
jsw = 8;
} else if (!strcmp(argv[i], "-m32")) {
jsw = 4;
} else if (!strcmp(argv[i], "-a")) {
build_atom_defines = TRUE;
} else if (!strcmp(argv[i], "--help")) {
return usage(argv[0]);
} else {
fprintf(stderr, "invalid argument '%s'\n", argv[i]);
return usage(argv[0]);
}
}
JSW = jsw;
if (build_atom_defines) {
dump_atom_defines();
return 0;
}
memset(s, 0, sizeof(*s));
init_list_head(&s->class_list);
/* add the predefined atoms (they have a corresponding define) */
for(i = 0; i < countof(atoms); i++) {
add_atom(&s->atom_list, atoms[i]);
}
/* add the predefined functions */
if (c_function_decl) {
const JSPropDef *d;
for(d = c_function_decl; d->def_type != JS_DEF_END; d++) {
if (d->def_type != JS_DEF_CFUNC) {
fprintf(stderr, "only C functions are allowed in c_function_decl[]\n");
exit(1);
}
add_atom(&s->atom_list, d->name);
add_cfunc(&s->cfunc_list,
d->name,
d->u.func.length,
d->u.func.magic,
d->u.func.cproto_name,
d->u.func.func_name);
}
}
/* first pass to define the atoms */
define_atoms_props(s, global_obj, PROPS_KIND_GLOBAL);
free_class_entries(s);
printf("/* this file is automatically generated - do not edit */\n\n");
printf("#include \"mquickjs_priv.h\"\n\n");
printf("static const uint%u_t __attribute((aligned(%d))) js_stdlib_table[] = {\n",
JSW * 8, ATOM_ALIGN);
dump_atoms(s);
s->global_object_offset = define_props(s, global_obj, PROPS_KIND_GLOBAL, NULL);
printf("};\n\n");
dump_cfuncs(s);
printf("#ifndef JS_CLASS_COUNT\n"
"#define JS_CLASS_COUNT JS_CLASS_USER /* total number of classes */\n"
"#endif\n\n");
dump_cfinalizers(s);
free_class_entries(s);
printf("const JSSTDLibraryDef %s = {\n", stdlib_name);
printf(" js_stdlib_table,\n");
printf(" js_c_function_table,\n");
printf(" js_c_finalizer_table,\n");
printf(" %d,\n", s->cur_offset);
printf(" %d,\n", ATOM_ALIGN);
printf(" %d,\n", s->sorted_atom_table_offset);
printf(" %d,\n", s->global_object_offset);
printf(" JS_CLASS_COUNT,\n");
printf("};\n\n");
return 0;
}

View File

@ -0,0 +1,97 @@
/*
* Micro QuickJS build utility
*
* Copyright (c) 2017-2025 Fabrice Bellard
* Copyright (c) 2017-2025 Charlie Gordon
*
* 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 MQUICKJS_BUILD_H
#define MQUICKJS_BUILD_H
#include <stdlib.h>
#include <inttypes.h>
enum {
JS_DEF_END,
JS_DEF_CFUNC,
JS_DEF_CGETSET,
JS_DEF_PROP_DOUBLE,
JS_DEF_PROP_UNDEFINED,
JS_DEF_PROP_STRING,
JS_DEF_PROP_NULL,
JS_DEF_CLASS,
};
typedef struct JSClassDef JSClassDef;
typedef struct JSPropDef {
int def_type;
const char *name;
union {
struct {
uint8_t length;
const char *magic;
const char *cproto_name;
const char *func_name;
} func;
struct {
const char *magic;
const char *cproto_name;
const char *get_func_name;
const char *set_func_name;
} getset;
double f64;
const JSClassDef *class1;
const char *str;
} u;
} JSPropDef;
typedef struct JSClassDef {
const char *name;
int length;
const char *cproto_name;
const char *func_name;
const char *class_id;
const JSPropDef *class_props; /* NULL if none */
const JSPropDef *proto_props; /* NULL if none */
const JSClassDef *parent_class; /* NULL if none */
const char *finalizer_name; /* "NULL" if none */
} JSClassDef;
#define JS_PROP_END { JS_DEF_END }
#define JS_CFUNC_DEF(name, length, func_name) { JS_DEF_CFUNC, name, { .func = { length, "0", "generic", #func_name } } }
#define JS_CFUNC_MAGIC_DEF(name, length, func_name, magic) { JS_DEF_CFUNC, name, { .func = { length, #magic, "generic_magic", #func_name } } }
#define JS_CFUNC_SPECIAL_DEF(name, length, proto, func_name) { JS_DEF_CFUNC, name, { .func = { length, "0", #proto, #func_name } } }
#define JS_CGETSET_DEF(name, get_name, set_name) { JS_DEF_CGETSET, name, { .getset = { "0", "generic", #get_name, #set_name } } }
#define JS_CGETSET_MAGIC_DEF(name, get_name, set_name, magic) { JS_DEF_CGETSET, name, { .getset = { #magic, "generic_magic", #get_name, #set_name } } }
#define JS_PROP_CLASS_DEF(name, cl) { JS_DEF_CLASS, name, { .class1 = cl } }
#define JS_PROP_DOUBLE_DEF(name, val, flags) { JS_DEF_PROP_DOUBLE, name, { .f64 = val } }
#define JS_PROP_UNDEFINED_DEF(name, flags) { JS_DEF_PROP_UNDEFINED, name }
#define JS_PROP_NULL_DEF(name, flags) { JS_DEF_PROP_NULL, name }
#define JS_PROP_STRING_DEF(name, cstr, flags) { JS_DEF_PROP_STRING, name, { .str = cstr } }
#define JS_CLASS_DEF(name, length, func_name, class_id, class_props, proto_props, parent_class, finalizer_name) { name, length, "constructor", #func_name, #class_id, class_props, proto_props, parent_class, #finalizer_name }
#define JS_CLASS_MAGIC_DEF(name, length, func_name, class_id, class_props, proto_props, parent_class, finalizer_name) { name, length, "constructor_magic", #func_name, #class_id, class_props, proto_props, parent_class, #finalizer_name }
#define JS_OBJECT_DEF(name, obj_props) { name, 0, NULL, NULL, NULL, obj_props, NULL, NULL, NULL }
int build_atoms(const char *stdlib_name, const JSPropDef *global_obj,
const JSPropDef *c_function_decl, int argc, char **argv);
#endif /* MQUICKJS_BUILD_H */

View File

@ -0,0 +1,264 @@
/*
* Micro QuickJS opcode definitions
*
* Copyright (c) 2017-2025 Fabrice Bellard
* Copyright (c) 2017-2025 Charlie Gordon
*
* 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.
*/
#ifdef FMT
FMT(none)
FMT(none_int)
FMT(none_loc)
FMT(none_arg)
FMT(none_var_ref)
FMT(u8)
FMT(i8)
FMT(loc8)
FMT(const8)
FMT(label8)
FMT(u16)
FMT(i16)
FMT(label16)
FMT(npop)
FMT(npopx)
FMT(loc)
FMT(arg)
FMT(var_ref)
FMT(u32)
FMT(i32)
FMT(const16)
FMT(label)
FMT(value)
#undef FMT
#endif /* FMT */
#ifdef DEF
#ifndef def
#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f)
#endif
DEF(invalid, 1, 0, 0, none) /* never emitted */
/* push values */
DEF( push_value, 5, 0, 1, value)
DEF( push_const, 3, 0, 1, const16)
DEF( fclosure, 3, 0, 1, const16)
DEF( undefined, 1, 0, 1, none)
DEF( null, 1, 0, 1, none)
DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */
DEF( push_false, 1, 0, 1, none)
DEF( push_true, 1, 0, 1, none)
DEF( object, 3, 0, 1, u16)
DEF( this_func, 1, 0, 1, none)
DEF( arguments, 1, 0, 1, none)
DEF( new_target, 1, 0, 1, none)
DEF( drop, 1, 1, 0, none) /* a -> */
DEF( nip, 1, 2, 1, none) /* a b -> b */
//DEF( nip1, 1, 3, 2, none) /* a b c -> b c */
DEF( dup, 1, 1, 2, none) /* a -> a a */
DEF( dup1, 1, 2, 3, none) /* a b -> a a b */
DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */
//DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */
DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */
DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */
//DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */
DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */
DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */
//DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */
DEF( swap, 1, 2, 2, none) /* a b -> b a */
//DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */
DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */
//DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */
//DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */
//DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */
DEF(call_constructor, 3, 1, 1, npop) /* func args... -> ret (arguments are not counted in n_pop) */
DEF( call, 3, 1, 1, npop) /* func args... -> ret (arguments are not counted in n_pop) */
DEF( call_method, 3, 2, 1, npop) /* this func args.. -> ret (arguments are not counted in n_pop) */
DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */
DEF( return, 1, 1, 0, none)
DEF( return_undef, 1, 0, 0, none)
DEF( throw, 1, 1, 0, none)
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a bytecode string */
DEF( get_field, 3, 1, 1, const16) /* obj -> val */
DEF( get_field2, 3, 1, 2, const16) /* obj -> obj val */
DEF( put_field, 3, 2, 0, const16) /* obj val -> */
DEF( get_array_el, 1, 2, 1, none) /* obj prop -> val */
DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */
DEF( put_array_el, 1, 3, 0, none) /* obj prop val -> */
DEF( get_length, 1, 1, 1, none) /* obj -> val */
DEF( get_length2, 1, 1, 2, none) /* obj -> obj val */
DEF( define_field, 3, 2, 1, const16) /* obj val -> obj */
DEF( define_getter, 3, 2, 1, const16) /* obj val -> obj */
DEF( define_setter, 3, 2, 1, const16) /* obj val -> obj */
DEF( set_proto, 1, 2, 1, none) /* obj proto -> obj */
DEF( get_loc, 3, 0, 1, loc)
DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */
DEF( get_arg, 3, 0, 1, arg)
DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */
DEF( get_var_ref, 3, 0, 1, var_ref)
DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */
DEF(get_var_ref_nocheck, 3, 0, 1, var_ref)
DEF(put_var_ref_nocheck, 3, 1, 0, var_ref)
DEF( if_false, 5, 1, 0, label)
DEF( if_true, 5, 1, 0, label) /* must come after if_false */
DEF( goto, 5, 0, 0, label) /* must come after if_true */
DEF( catch, 5, 0, 1, label)
DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
DEF( ret, 1, 1, 0, none) /* used to return from the finally block */
DEF( for_in_start, 1, 1, 1, none) /* obj -> iter */
DEF( for_of_start, 1, 1, 1, none) /* obj -> iter */
DEF( for_of_next, 1, 1, 3, none) /* iter -> iter val done */
/* arithmetic/logic operations */
DEF( neg, 1, 1, 1, none)
DEF( plus, 1, 1, 1, none)
DEF( dec, 1, 1, 1, none)
DEF( inc, 1, 1, 1, none)
DEF( post_dec, 1, 1, 2, none)
DEF( post_inc, 1, 1, 2, none)
DEF( not, 1, 1, 1, none)
DEF( lnot, 1, 1, 1, none)
DEF( typeof, 1, 1, 1, none)
DEF( delete, 1, 2, 1, none) /* obj prop -> ret */
DEF( mul, 1, 2, 1, none)
DEF( div, 1, 2, 1, none)
DEF( mod, 1, 2, 1, none)
DEF( add, 1, 2, 1, none)
DEF( sub, 1, 2, 1, none)
DEF( pow, 1, 2, 1, none)
DEF( shl, 1, 2, 1, none)
DEF( sar, 1, 2, 1, none)
DEF( shr, 1, 2, 1, none)
DEF( lt, 1, 2, 1, none)
DEF( lte, 1, 2, 1, none)
DEF( gt, 1, 2, 1, none)
DEF( gte, 1, 2, 1, none)
DEF( instanceof, 1, 2, 1, none)
DEF( in, 1, 2, 1, none)
DEF( eq, 1, 2, 1, none)
DEF( neq, 1, 2, 1, none)
DEF( strict_eq, 1, 2, 1, none)
DEF( strict_neq, 1, 2, 1, none)
DEF( and, 1, 2, 1, none)
DEF( xor, 1, 2, 1, none)
DEF( or, 1, 2, 1, none)
/* must be the last non short and non temporary opcode */
DEF( nop, 1, 0, 0, none)
DEF( push_minus1, 1, 0, 1, none_int)
DEF( push_0, 1, 0, 1, none_int)
DEF( push_1, 1, 0, 1, none_int)
DEF( push_2, 1, 0, 1, none_int)
DEF( push_3, 1, 0, 1, none_int)
DEF( push_4, 1, 0, 1, none_int)
DEF( push_5, 1, 0, 1, none_int)
DEF( push_6, 1, 0, 1, none_int)
DEF( push_7, 1, 0, 1, none_int)
DEF( push_i8, 2, 0, 1, i8)
DEF( push_i16, 3, 0, 1, i16)
DEF( push_const8, 2, 0, 1, const8)
DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */
DEF(push_empty_string, 1, 0, 1, none)
DEF( get_loc8, 2, 0, 1, loc8)
DEF( put_loc8, 2, 1, 0, loc8) /* must follow get_loc8 */
DEF( get_loc0, 1, 0, 1, none_loc)
DEF( get_loc1, 1, 0, 1, none_loc)
DEF( get_loc2, 1, 0, 1, none_loc)
DEF( get_loc3, 1, 0, 1, none_loc)
DEF( put_loc0, 1, 1, 0, none_loc) /* must follow get_loc */
DEF( put_loc1, 1, 1, 0, none_loc)
DEF( put_loc2, 1, 1, 0, none_loc)
DEF( put_loc3, 1, 1, 0, none_loc)
DEF( get_arg0, 1, 0, 1, none_arg)
DEF( get_arg1, 1, 0, 1, none_arg)
DEF( get_arg2, 1, 0, 1, none_arg)
DEF( get_arg3, 1, 0, 1, none_arg)
DEF( put_arg0, 1, 1, 0, none_arg) /* must follow get_arg */
DEF( put_arg1, 1, 1, 0, none_arg)
DEF( put_arg2, 1, 1, 0, none_arg)
DEF( put_arg3, 1, 1, 0, none_arg)
#if 0
DEF( if_false8, 2, 1, 0, label8)
DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */
DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */
DEF( goto16, 3, 0, 0, label16)
DEF( call0, 1, 1, 1, npopx)
DEF( call1, 1, 1, 1, npopx)
DEF( call2, 1, 1, 1, npopx)
DEF( call3, 1, 1, 1, npopx)
#endif
#undef DEF
#undef def
#endif /* DEF */
#ifdef REDEF
/* regular expression bytecode */
REDEF(invalid, 1) /* never used */
REDEF(char1, 2)
REDEF(char2, 3)
REDEF(char3, 4)
REDEF(char4, 5)
REDEF(dot, 1)
REDEF(any, 1) /* same as dot but match any character including line terminator */
REDEF(space, 1)
REDEF(not_space, 1) /* must come after */
REDEF(line_start, 1)
REDEF(line_start_m, 1)
REDEF(line_end, 1)
REDEF(line_end_m, 1)
REDEF(goto, 5)
REDEF(split_goto_first, 5)
REDEF(split_next_first, 5)
REDEF(match, 1)
REDEF(lookahead_match, 1)
REDEF(negative_lookahead_match, 1) /* must come after */
REDEF(save_start, 2) /* save start position */
REDEF(save_end, 2) /* save end position, must come after saved_start */
REDEF(save_reset, 3) /* reset save positions */
REDEF(loop, 6) /* decrement the top the stack and goto if != 0 */
REDEF(loop_split_goto_first, 10) /* loop and then split */
REDEF(loop_split_next_first, 10)
REDEF(loop_check_adv_split_goto_first, 10) /* loop and then check advance and split */
REDEF(loop_check_adv_split_next_first, 10)
REDEF(set_i32, 6) /* store the immediate value to a register */
REDEF(word_boundary, 1)
REDEF(not_word_boundary, 1)
REDEF(back_reference, 2)
REDEF(back_reference_i, 2)
REDEF(range8, 2) /* variable length */
REDEF(range, 3) /* variable length */
REDEF(lookahead, 5)
REDEF(negative_lookahead, 5) /* must come after */
REDEF(set_char_pos, 2) /* store the character position to a register */
REDEF(check_advance, 2) /* check that the register is different from the character position */
#endif /* REDEF */

View File

@ -0,0 +1,268 @@
/* microj private definitions */
#ifndef MICROJS_PRIV_H
#define MICROJS_PRIV_H
#include "mquickjs.h"
#include "libm.h"
#define JS_DUMP /* 2.6 kB */
//#define DUMP_EXEC
//#define DUMP_FUNC_BYTECODE /* dump the bytecode of each compiled function */
//#define DUMP_REOP /* dump regexp bytecode */
//#define DUMP_GC
//#define DUMP_TOKEN /* dump parsed tokens */
/* run GC before at each malloc() and modify the allocated data pointers */
//#define DEBUG_GC
#if defined(DUMP_FUNC_BYTECODE) || defined(DUMP_EXEC)
#define DUMP_BYTECODE /* include the dump_byte_code() function */
#endif
#define JS_VALUE_TO_PTR(v) (void *)((uintptr_t)(v) - 1)
#define JS_VALUE_FROM_PTR(ptr) (JSWord)((uintptr_t)(ptr) + 1)
#define JS_IS_ROM_PTR(ctx, ptr) ((uintptr_t)(ptr) < (uintptr_t)ctx || (uintptr_t)(ptr) >= (uintptr_t)ctx->stack_top)
enum {
JS_MTAG_FREE,
/* javascript values */
JS_MTAG_OBJECT,
JS_MTAG_FLOAT64,
JS_MTAG_STRING,
/* other special memory blocks */
JS_MTAG_FUNCTION_BYTECODE,
JS_MTAG_VALUE_ARRAY,
JS_MTAG_BYTE_ARRAY,
JS_MTAG_VARREF,
JS_MTAG_COUNT,
};
/* JS_MTAG_BITS bits are reserved at the start of every memory block */
#define JS_MTAG_BITS 4
#define JS_MB_HEADER \
JSWord gc_mark: 1; \
JSWord mtag: (JS_MTAG_BITS - 1)
typedef enum {
JS_PROP_NORMAL,
JS_PROP_GETSET, /* value is a two element JSValueArray */
JS_PROP_VARREF, /* value is a JSVarRef (used for global variables) */
JS_PROP_SPECIAL, /* for the prototype and constructor properties in ROM */
} JSPropTypeEnum;
#define JS_MB_HEADER_DEF(tag) ((tag) << 1)
#define JS_VALUE_ARRAY_HEADER(size) (JS_MB_HEADER_DEF(JS_MTAG_VALUE_ARRAY) | ((size) << JS_MTAG_BITS))
#define JS_ROM_VALUE(offset) JS_VALUE_FROM_PTR(&js_stdlib_table[offset])
/* runtime helpers */
JSValue js_function_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_function_get_prototype(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_function_set_prototype(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_function_get_length_name(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int is_name);
JSValue js_function_toString(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_function_call(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_function_apply(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_function_bind(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_function_bound(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, JSValue params);
JSValue js_array_get_length(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_set_length(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_number_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_number_toString(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_number_toFixed(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_number_toExponential(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_number_toPrecision(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_number_parseInt(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_number_parseFloat(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_boolean_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_string_get_length(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_string_set_length(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_string_slice(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_string_substring(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
enum {
magic_internalAt,
magic_charAt,
magic_charCodeAt,
magic_codePointAt,
};
JSValue js_string_charAt(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int magic);
JSValue js_string_concat(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_string_indexOf(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int lastIndexOf);
JSValue js_string_match(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_string_replace(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int is_replaceAll);
JSValue js_string_search(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_string_split(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_string_toLowerCase(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int to_lower);
JSValue js_string_trim(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int magic);
JSValue js_string_toString(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_string_repeat(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_object_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_object_defineProperty(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_object_getPrototypeOf(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_object_setPrototypeOf(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_object_create(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_object_keys(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_object_hasOwnProperty(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_object_toString(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_string_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_string_fromCharCode(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int is_fromCodePoint);
JSValue js_error_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int magic);
JSValue js_error_toString(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_error_get_message(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int magic);
JSValue js_array_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_push(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int is_unshift);
JSValue js_array_pop(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_shift(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_join(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_toString(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_isArray(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_reverse(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_concat(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_indexOf(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int is_lastIndexOf);
JSValue js_array_slice(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_splice(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_sort(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
#define js_special_every 0
#define js_special_some 1
#define js_special_forEach 2
#define js_special_map 3
#define js_special_filter 4
JSValue js_array_every(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int special);
#define js_special_reduce 0
#define js_special_reduceRight 1
JSValue js_array_reduce(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int special);
JSValue js_math_min_max(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int magic);
double js_math_sign(double a);
double js_math_fround(double a);
JSValue js_math_imul(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_math_clz32(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_math_atan2(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_math_pow(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_math_random(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_buffer_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_array_buffer_get_byteLength(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_typed_array_base_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_typed_array_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int magic);
JSValue js_typed_array_get_length(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int magic);
JSValue js_typed_array_subarray(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_typed_array_set(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_date_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_global_eval(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_global_isNaN(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_global_isFinite(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_json_parse(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_json_stringify(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_regexp_constructor(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_regexp_get_lastIndex(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_regexp_set_lastIndex(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_regexp_get_source(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_regexp_get_flags(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv);
JSValue js_regexp_exec(JSContext *ctx, JSValue *this_val,
int argc, JSValue *argv, int is_test);
#endif /* MICROJS_PRIV_H */

18
lib/mquickjs/scripts/build.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
set -e
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd $SCRIPT_DIR/.. # mquickjs directory
make clean
CONFIG_X86_32=1 CONFIG_SOFTFLOAT=1 make mqjs_stdlib.h
CONFIG_X86_32=1 CONFIG_SOFTFLOAT=1 make mquickjs_atom.h
mv mqjs_stdlib.h crosspoint_stdlib.h
mv mquickjs_atom.h crosspoint_atom.h # avoid being deleted in the next step
make clean
mv crosspoint_atom.h mquickjs_atom.h

View File

@ -0,0 +1,566 @@
/*
* Micro QuickJS REPL library
*
* Copyright (c) 2017-2025 Fabrice Bellard
* Copyright (c) 2017-2025 Charlie Gordon
*
* 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 <math.h>
#include <stdio.h>
#include <string.h>
#include "mquickjs_build.h"
/* defined in mqjs_example.c */
//#define CONFIG_CLASS_EXAMPLE
static const JSPropDef js_object_proto[] = {
JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty),
JS_CFUNC_DEF("toString", 0, js_object_toString),
JS_PROP_END,
};
static const JSPropDef js_object[] = {
JS_CFUNC_DEF("defineProperty", 3, js_object_defineProperty),
JS_CFUNC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf),
JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf),
JS_CFUNC_DEF("create", 2, js_object_create),
JS_CFUNC_DEF("keys", 1, js_object_keys),
JS_PROP_END,
};
static const JSClassDef js_object_class =
JS_CLASS_DEF("Object", 1, js_object_constructor, JS_CLASS_OBJECT,
js_object, js_object_proto, NULL, NULL);
static const JSPropDef js_function_proto[] = {
JS_CGETSET_DEF("prototype", js_function_get_prototype, js_function_set_prototype ),
JS_CFUNC_DEF("call", 1, js_function_call ),
JS_CFUNC_DEF("apply", 2, js_function_apply ),
JS_CFUNC_DEF("bind", 1, js_function_bind ),
JS_CFUNC_DEF("toString", 0, js_function_toString ),
JS_CGETSET_MAGIC_DEF("length", js_function_get_length_name, NULL, 0 ),
JS_CGETSET_MAGIC_DEF("name", js_function_get_length_name, NULL, 1 ),
JS_PROP_END,
};
static const JSClassDef js_function_class =
JS_CLASS_DEF("Function", 1, js_function_constructor, JS_CLASS_CLOSURE, NULL, js_function_proto, NULL, NULL);
static const JSPropDef js_number_proto[] = {
JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ),
JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ),
JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ),
JS_CFUNC_DEF("toString", 1, js_number_toString ),
JS_PROP_END,
};
static const JSPropDef js_number[] = {
JS_CFUNC_DEF("parseInt", 2, js_number_parseInt ),
JS_CFUNC_DEF("parseFloat", 1, js_number_parseFloat ),
JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),
JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),
JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),
JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),
JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */
JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */
JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */
JS_PROP_END,
};
static const JSClassDef js_number_class =
JS_CLASS_DEF("Number", 1, js_number_constructor, JS_CLASS_NUMBER, js_number, js_number_proto, NULL, NULL);
static const JSClassDef js_boolean_class =
JS_CLASS_DEF("Boolean", 1, js_boolean_constructor, JS_CLASS_BOOLEAN, NULL, NULL, NULL, NULL);
static const JSPropDef js_string_proto[] = {
JS_CGETSET_DEF("length", js_string_get_length, js_string_set_length ),
JS_CFUNC_MAGIC_DEF("charAt", 1, js_string_charAt, magic_charAt ),
JS_CFUNC_MAGIC_DEF("charCodeAt", 1, js_string_charAt, magic_charCodeAt ),
JS_CFUNC_MAGIC_DEF("codePointAt", 1, js_string_charAt, magic_codePointAt ),
JS_CFUNC_DEF("slice", 2, js_string_slice ),
JS_CFUNC_DEF("substring", 2, js_string_substring ),
JS_CFUNC_DEF("concat", 1, js_string_concat ),
JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),
JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),
JS_CFUNC_DEF("match", 1, js_string_match ),
JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),
JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),
JS_CFUNC_DEF("search", 1, js_string_search ),
JS_CFUNC_DEF("split", 2, js_string_split ),
JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),
JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),
JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),
JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),
JS_CFUNC_DEF("toString", 0, js_string_toString ),
JS_CFUNC_DEF("repeat", 1, js_string_repeat ),
JS_PROP_END,
};
static const JSPropDef js_string[] = {
JS_CFUNC_MAGIC_DEF("fromCharCode", 1, js_string_fromCharCode, 0 ),
JS_CFUNC_MAGIC_DEF("fromCodePoint", 1, js_string_fromCharCode, 1 ),
JS_PROP_END,
};
static const JSClassDef js_string_class =
JS_CLASS_DEF("String", 1, js_string_constructor, JS_CLASS_STRING, js_string, js_string_proto, NULL, NULL);
static const JSPropDef js_array_proto[] = {
JS_CFUNC_DEF("concat", 1, js_array_concat ),
JS_CGETSET_DEF("length", js_array_get_length, js_array_set_length ),
JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),
JS_CFUNC_DEF("pop", 0, js_array_pop ),
JS_CFUNC_DEF("join", 1, js_array_join ),
JS_CFUNC_DEF("toString", 0, js_array_toString ),
JS_CFUNC_DEF("reverse", 0, js_array_reverse ),
JS_CFUNC_DEF("shift", 0, js_array_shift ),
JS_CFUNC_DEF("slice", 2, js_array_slice ),
JS_CFUNC_DEF("splice", 2, js_array_splice ),
JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),
JS_CFUNC_MAGIC_DEF("indexOf", 1, js_array_indexOf, 0 ),
JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_array_indexOf, 1 ),
JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, js_special_every ),
JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, js_special_some ),
JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, js_special_forEach ),
JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, js_special_map ),
JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, js_special_filter ),
JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, js_special_reduce ),
JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, js_special_reduceRight ),
JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, js_special_reduce ),
JS_CFUNC_DEF("sort", 1, js_array_sort ),
JS_PROP_END,
};
static const JSPropDef js_array[] = {
JS_CFUNC_DEF("isArray", 1, js_array_isArray ),
JS_PROP_END,
};
static const JSClassDef js_array_class =
JS_CLASS_DEF("Array", 1, js_array_constructor, JS_CLASS_ARRAY, js_array, js_array_proto, NULL, NULL);
static const JSPropDef js_error_proto[] = {
JS_CFUNC_DEF("toString", 0, js_error_toString ),
JS_PROP_STRING_DEF("name", "Error", 0 ),
JS_CGETSET_MAGIC_DEF("message", js_error_get_message, NULL, 0 ),
JS_CGETSET_MAGIC_DEF("stack", js_error_get_message, NULL, 1 ),
JS_PROP_END,
};
static const JSClassDef js_error_class =
JS_CLASS_MAGIC_DEF("Error", 1, js_error_constructor, JS_CLASS_ERROR, NULL, js_error_proto, NULL, NULL);
#define ERROR_DEF(cname, name, class_id) \
static const JSPropDef js_ ## cname ## _proto[] = { \
JS_PROP_STRING_DEF("name", name, 0 ), \
JS_PROP_END, \
}; \
static const JSClassDef js_ ## cname ## _class = \
JS_CLASS_MAGIC_DEF(name, 1, js_error_constructor, class_id, NULL, js_ ## cname ## _proto, &js_error_class, NULL);
ERROR_DEF(eval_error, "EvalError", JS_CLASS_EVAL_ERROR)
ERROR_DEF(range_error, "RangeError", JS_CLASS_RANGE_ERROR)
ERROR_DEF(reference_error, "ReferenceError", JS_CLASS_REFERENCE_ERROR)
ERROR_DEF(syntax_error, "SyntaxError", JS_CLASS_SYNTAX_ERROR)
ERROR_DEF(type_error, "TypeError", JS_CLASS_TYPE_ERROR)
ERROR_DEF(uri_error, "URIError", JS_CLASS_URI_ERROR)
ERROR_DEF(internal_error, "InternalError", JS_CLASS_INTERNAL_ERROR)
static const JSPropDef js_math[] = {
JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),
JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, js_fabs ),
JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, js_floor ),
JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, js_ceil ),
JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_round_inf ),
JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, js_sqrt ),
JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),
JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),
JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),
JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),
JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),
JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),
JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),
JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),
JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, js_sin ),
JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, js_cos ),
JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, js_tan ),
JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, js_asin ),
JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, js_acos ),
JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, js_atan ),
JS_CFUNC_DEF("atan2", 2, js_math_atan2 ),
JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, js_exp ),
JS_CFUNC_SPECIAL_DEF("log", 1, f_f, js_log ),
JS_CFUNC_DEF("pow", 2, js_math_pow ),
JS_CFUNC_DEF("random", 0, js_math_random ),
/* some ES6 functions */
JS_CFUNC_DEF("imul", 2, js_math_imul ),
JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),
JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),
JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, js_trunc ),
JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, js_log2 ),
JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, js_log10 ),
JS_PROP_END,
};
static const JSClassDef js_math_obj =
JS_OBJECT_DEF("Math", js_math);
static const JSPropDef js_json[] = {
JS_CFUNC_DEF("parse", 2, js_json_parse ),
JS_CFUNC_DEF("stringify", 3, js_json_stringify ),
JS_PROP_END,
};
static const JSClassDef js_json_obj =
JS_OBJECT_DEF("JSON", js_json);
/* typed arrays */
static const JSPropDef js_array_buffer_proto[] = {
JS_CGETSET_DEF("byteLength", js_array_buffer_get_byteLength, NULL ),
JS_PROP_END,
};
static const JSClassDef js_array_buffer_class =
JS_CLASS_DEF("ArrayBuffer", 1, js_array_buffer_constructor, JS_CLASS_ARRAY_BUFFER, NULL, js_array_buffer_proto, NULL, NULL);
static const JSPropDef js_typed_array_base_proto[] = {
JS_CGETSET_MAGIC_DEF("length", js_typed_array_get_length, NULL, 0 ),
JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_length, NULL, 1 ),
JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_length, NULL, 2 ),
JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_length, NULL, 3 ),
JS_CFUNC_DEF("join", 1, js_array_join ),
JS_CFUNC_DEF("toString", 0, js_array_toString ),
JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ),
JS_CFUNC_DEF("set", 1, js_typed_array_set ),
JS_PROP_END,
};
static const JSClassDef js_typed_array_base_class =
JS_CLASS_DEF("TypedArray", 0, js_typed_array_base_constructor, JS_CLASS_TYPED_ARRAY, NULL, js_typed_array_base_proto, NULL, NULL);
#define TA_DEF(name, class_name, bpe)\
static const JSPropDef js_ ## name [] = {\
JS_PROP_DOUBLE_DEF("BYTES_PER_ELEMENT", bpe, 0),\
JS_PROP_END,\
};\
static const JSPropDef js_ ## name ## _proto[] = {\
JS_PROP_DOUBLE_DEF("BYTES_PER_ELEMENT", bpe, 0),\
JS_PROP_END,\
};\
static const JSClassDef js_ ## name ## _class =\
JS_CLASS_MAGIC_DEF(#name, 3, js_typed_array_constructor, class_name, js_ ## name, js_ ## name ## _proto, &js_typed_array_base_class, NULL);
TA_DEF(Uint8ClampedArray, JS_CLASS_UINT8C_ARRAY, 1)
TA_DEF(Int8Array, JS_CLASS_INT8_ARRAY, 1)
TA_DEF(Uint8Array, JS_CLASS_UINT8_ARRAY, 1)
TA_DEF(Int16Array, JS_CLASS_INT16_ARRAY, 2)
TA_DEF(Uint16Array, JS_CLASS_UINT16_ARRAY, 2)
TA_DEF(Int32Array, JS_CLASS_INT32_ARRAY, 4)
TA_DEF(Uint32Array, JS_CLASS_UINT32_ARRAY, 4)
TA_DEF(Float32Array, JS_CLASS_FLOAT32_ARRAY, 4)
TA_DEF(Float64Array, JS_CLASS_FLOAT64_ARRAY, 8)
/* regexp */
static const JSPropDef js_regexp_proto[] = {
JS_CGETSET_DEF("lastIndex", js_regexp_get_lastIndex, js_regexp_set_lastIndex ),
JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),
JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),
JS_CFUNC_MAGIC_DEF("exec", 1, js_regexp_exec, 0 ),
JS_CFUNC_MAGIC_DEF("test", 1, js_regexp_exec, 1 ),
JS_PROP_END,
};
static const JSClassDef js_regexp_class =
JS_CLASS_DEF("RegExp", 2, js_regexp_constructor, JS_CLASS_REGEXP, NULL, js_regexp_proto, NULL, NULL);
/* other objects */
static const JSPropDef js_date[] = {
JS_CFUNC_DEF("now", 0, js_date_now),
JS_PROP_END,
};
static const JSClassDef js_date_class =
JS_CLASS_DEF("Date", 7, js_date_constructor, JS_CLASS_DATE, js_date, NULL, NULL, NULL);
static const JSPropDef js_console[] = {
JS_CFUNC_DEF("log", 1, js_print),
JS_PROP_END,
};
static const JSClassDef js_console_obj =
JS_OBJECT_DEF("Console", js_console);
static const JSPropDef js_performance[] = {
JS_CFUNC_DEF("now", 0, js_performance_now),
JS_PROP_END,
};
static const JSClassDef js_performance_obj =
JS_OBJECT_DEF("Performance", js_performance);
// Crosspoint-specific functions (will invoke native C++ code)
#define JS_PROP_INT_DEF(name, val, flags) { JS_DEF_PROP_DOUBLE, name, { .f64 = val } }
static const JSPropDef js_cp[] = {
JS_PROP_STRING_DEF("FAST_REFRESH", "F", 0),
JS_PROP_STRING_DEF("HALF_REFRESH", "H", 0),
JS_PROP_STRING_DEF("FULL_REFRESH", "A", 0),
JS_PROP_STRING_DEF("FONT_UI_10", "UI10", 0),
JS_PROP_STRING_DEF("FONT_UI_12", "UI12", 0),
JS_PROP_STRING_DEF("FONT_SMALL", "SM", 0),
JS_PROP_STRING_DEF("TEXT_REGULAR", "R", 0),
JS_PROP_STRING_DEF("TEXT_BOLD", "B", 0),
JS_PROP_STRING_DEF("TEXT_ITALIC", "I", 0),
JS_PROP_STRING_DEF("TEXT_BOLD_ITALIC", "J", 0),
JS_PROP_STRING_DEF("BTN_BACK", "B", 0),
JS_PROP_STRING_DEF("BTN_CONFIRM", "C", 0),
JS_PROP_STRING_DEF("BTN_LEFT", "L", 0),
JS_PROP_STRING_DEF("BTN_RIGHT", "R", 0),
JS_PROP_STRING_DEF("BTN_UP", "U", 0),
JS_PROP_STRING_DEF("BTN_DOWN", "D", 0),
/**
* @brief Get the number of milliseconds since the device was powered on
* @return {number} Milliseconds since power on
*/
JS_CFUNC_DEF("millis", 0, js_millis),
/**
* @brief Check if a button is currently pressed
* @param {string} buttonId The ID of the button to check (e.g., CP.BTN_BACK, CP.BTN_CONFIRM, etc.)
* @return {boolean} True if the button is pressed, false otherwise
*/
JS_CFUNC_DEF("btnIsPressed", 0, js_btnIsPressed),
/**
* @brief Get screen width, ben changed based on orientation
* @return {number} Screen width in pixels
*/
JS_CFUNC_DEF("getScreenWidth", 0, js_getScreenWidth),
/**
* @brief Get screen height, ben changed based on orientation
* @return {number} Screen height in pixels
*/
JS_CFUNC_DEF("getScreenHeight", 0, js_getScreenHeight),
/**
* @brief Clear the screen buffer
* @param {number} color The color to clear the screen to (0-255, where 0 = black, 255 = white)
* @return {void}
*/
JS_CFUNC_DEF("clearScreen", 1, js_clearScreen),
/**
* @brief Display the screen buffer
* @param {string} refreshMode The refresh mode to use (0 = CP.FAST_REFRESH, 1 = CP.FULL_REFRESH, etc.)
* @return {void}
*/
JS_CFUNC_DEF("displayBuffer", 1, js_displayBuffer),
/**
* @brief Draws a line from (x1, y1) to (x2, y2).
* @param x1 {number} The x-coordinate of the starting point.
* @param y1 {number} The y-coordinate of the starting point.
* @param x2 {number} The x-coordinate of the ending point.
* @param y2 {number} The y-coordinate of the ending point.
* @param state {boolean} The state to set the pixels to (true for on, false for off). Defaults to true.
*/
JS_CFUNC_DEF("drawLine", 5, js_drawLine),
/**
* @brief Draws the outline of a rectangle at the specified position with the given dimensions.
* @param x {number} The x-coordinate of the top-left corner of the rectangle.
* @param y {number} The y-coordinate of the top-left corner of the rectangle.
* @param width {number} The width of the rectangle.
* @param height {number} The height of the rectangle.
* @param state {boolean} The state to set the pixels to (true for on, false for off). Defaults to true.
*/
JS_CFUNC_DEF("drawRect", 5, js_drawRect),
/**
* @brief Fills a rectangle at the specified position with the given dimensions.
* @param x {number} The x-coordinate of the top-left corner of the rectangle.
* @param y {number} The y-coordinate of the top-left corner of the rectangle.
* @param width {number} The width of the rectangle.
* @param height {number} The height of the rectangle.
* @param state {boolean} The state to set the pixels to (true for on, false for off). Defaults to true.
*/
JS_CFUNC_DEF("fillRect", 5, js_fillRect),
/**
* @brief Draws a bitmap image at the specified position with the given dimensions.
* @param bitmap {ArrayBuffer} The array of bytes representing the bitmap data, 1bpp, must have total size of (width * height) / 8.
* @param x {number} The x-coordinate of the top-left corner where the image will be drawn.
* @param y {number} The y-coordinate of the top-left corner where the image will be drawn.
* @param width {number} The width of the image.
* @param height {number} The height of the image.
*/
JS_CFUNC_DEF("drawImage", 5, js_drawImage),
/**
* @brief Get the width in pixels of the text.
* @param fontId {string} The ID of the font to use (e.g., CP.FONT_UI_10, CP.FONT_SMALL, etc.)
* @param text {string} The text to measure.
* @param style {string} The style of the font (CP.TEXT_REGULAR, CP.TEXT_BOLD, etc.)
*/
JS_CFUNC_DEF("getTextWidth", 3, js_getTextWidth),
/**
* @brief Draws centered text at the specified y-coordinate (non-wrapped).
* @param fontId {string} The ID of the font to use (e.g., CP.FONT_UI_10, CP.FONT_SMALL, etc.)
* @param y {number} The y-coordinate where the text will be drawn.
* @param text {string} The text to draw.
* @param black {boolean} Whether to draw the text in black (true) or white (false).
* @param style {string} The style of the font (CP.TEXT_REGULAR, CP.TEXT_BOLD, etc.)
*/
JS_CFUNC_DEF("drawCenteredText", 5, js_drawCenteredText),
/**
* @brief Draws text at the specified position (non-wrapped).
* @param fontId {string} The ID of the font to use (e.g., CP.FONT_UI_10, CP.FONT_SMALL, etc.)
* @param x {number} The x-coordinate where the text will be drawn.
* @param y {number} The y-coordinate where the text will be drawn.
* @param text {string} The text to draw.
* @param black {boolean} Whether to draw the text in black (true) or white (false).
* @param style {string} The style of the font (CP.TEXT_REGULAR, CP.TEXT_BOLD, etc.)
*/
JS_CFUNC_DEF("drawText", 6, js_drawText),
// JS_CFUNC_DEF("drawTextBox", 6, js_drawText), // TODO: implement drawTextBox
/**
* @brief Draws button hints at the bottom of the screen.
* @param fontId {string} The ID of the font to use (e.g., CP.FONT_UI_10, CP.FONT_SMALL, etc.)
* @param btn1Text {string} The text for button 1.
* @param btn2Text {string} The text for button 2.
* @param btn3Text {string} The text for button 3.
* @param btn4Text {string} The text for button 4.
*/
JS_CFUNC_DEF("drawButtonHints", 5, js_drawButtonHints),
/**
* @brief Draws side button hints on the left and right sides of the screen.
* @param fontId {string} The ID of the font to use (e.g., CP.FONT_UI_10, CP.FONT_SMALL, etc.)
* @param topBtnText {string} The text for the top side button.
* @param bottomBtnText {string} The text for the bottom side button.
*/
JS_CFUNC_DEF("drawSideButtonHints", 3, js_drawSideButtonHints),
JS_PROP_END,
};
static const JSClassDef js_cp_obj =
JS_OBJECT_DEF("CP", js_cp);
// End of Crosspoint-specific functions
static const JSPropDef js_global_object[] = {
JS_PROP_CLASS_DEF("Object", &js_object_class),
JS_PROP_CLASS_DEF("Function", &js_function_class),
JS_PROP_CLASS_DEF("Number", &js_number_class),
JS_PROP_CLASS_DEF("Boolean", &js_boolean_class),
JS_PROP_CLASS_DEF("String", &js_string_class),
JS_PROP_CLASS_DEF("Array", &js_array_class),
JS_PROP_CLASS_DEF("Math", &js_math_obj),
JS_PROP_CLASS_DEF("Date", &js_date_class),
JS_PROP_CLASS_DEF("JSON", &js_json_obj),
JS_PROP_CLASS_DEF("RegExp", &js_regexp_class),
JS_PROP_CLASS_DEF("Error", &js_error_class),
JS_PROP_CLASS_DEF("EvalError", &js_eval_error_class),
JS_PROP_CLASS_DEF("RangeError", &js_range_error_class),
JS_PROP_CLASS_DEF("ReferenceError", &js_reference_error_class),
JS_PROP_CLASS_DEF("SyntaxError", &js_syntax_error_class),
JS_PROP_CLASS_DEF("TypeError", &js_type_error_class),
JS_PROP_CLASS_DEF("URIError", &js_uri_error_class),
JS_PROP_CLASS_DEF("InternalError", &js_internal_error_class),
JS_PROP_CLASS_DEF("ArrayBuffer", &js_array_buffer_class),
JS_PROP_CLASS_DEF("Uint8ClampedArray", &js_Uint8ClampedArray_class),
JS_PROP_CLASS_DEF("Int8Array", &js_Int8Array_class),
JS_PROP_CLASS_DEF("Uint8Array", &js_Uint8Array_class),
JS_PROP_CLASS_DEF("Int16Array", &js_Int16Array_class),
JS_PROP_CLASS_DEF("Uint16Array", &js_Uint16Array_class),
JS_PROP_CLASS_DEF("Int32Array", &js_Int32Array_class),
JS_PROP_CLASS_DEF("Uint32Array", &js_Uint32Array_class),
JS_PROP_CLASS_DEF("Float32Array", &js_Float32Array_class),
JS_PROP_CLASS_DEF("Float64Array", &js_Float64Array_class),
JS_CFUNC_DEF("parseInt", 2, js_number_parseInt ),
JS_CFUNC_DEF("parseFloat", 1, js_number_parseFloat ),
JS_CFUNC_DEF("eval", 1, js_global_eval),
JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
JS_PROP_UNDEFINED_DEF("undefined", 0 ),
/* Note: null is expanded as the global object in js_global_object[] */
JS_PROP_NULL_DEF("globalThis", 0 ),
JS_PROP_CLASS_DEF("console", &js_console_obj),
JS_PROP_CLASS_DEF("performance", &js_performance_obj),
JS_CFUNC_DEF("print", 1, js_print),
#ifdef CONFIG_CLASS_EXAMPLE
JS_PROP_CLASS_DEF("Rectangle", &js_rectangle_class),
JS_PROP_CLASS_DEF("FilledRectangle", &js_filled_rectangle_class),
#else
JS_CFUNC_DEF("gc", 0, js_gc),
JS_CFUNC_DEF("load", 1, js_load),
JS_CFUNC_DEF("setTimeout", 2, js_setTimeout),
JS_CFUNC_DEF("clearTimeout", 1, js_clearTimeout),
#endif
JS_PROP_CLASS_DEF("CP", &js_cp_obj), // crosspoint-specific object
JS_PROP_END,
};
/* Additional C function declarations (only useful for C
closures). They are always defined first. */
static const JSPropDef js_c_function_decl[] = {
/* must come first if "bind" is defined */
JS_CFUNC_SPECIAL_DEF("bound", 0, generic_params, js_function_bound ),
#ifdef CONFIG_CLASS_EXAMPLE
JS_CFUNC_SPECIAL_DEF("rectangle_closure_test", 0, generic_params, js_rectangle_closure_test ),
#endif
JS_PROP_END,
};
int main(int argc, char **argv)
{
return build_atoms("js_stdlib", js_global_object, js_c_function_decl, argc, argv);
}

View File

@ -0,0 +1,932 @@
/*
* Micro QuickJS build utility
*
* Copyright (c) 2017-2025 Fabrice Bellard
* Copyright (c) 2017-2025 Charlie Gordon
*
* 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 <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include "cutils.h"
#include "list.h"
#include "mquickjs_build.h"
static unsigned JSW = 4; // override this with -m64
typedef struct {
char *str;
int offset;
} AtomDef;
typedef struct {
AtomDef *tab;
int count;
int size;
int offset;
} AtomList;
typedef struct {
char *name;
int length;
char *magic;
char *cproto_name;
char *cfunc_name;
} CFuncDef;
typedef struct {
CFuncDef *tab;
int count;
int size;
} CFuncList;
typedef struct {
struct list_head link;
const JSClassDef *class1;
int class_idx;
char *finalizer_name;
char *class_id;
} ClassDefEntry;
typedef struct {
AtomList atom_list;
CFuncList cfunc_list;
int cur_offset;
int sorted_atom_table_offset;
int global_object_offset;
struct list_head class_list;
} BuildContext;
static const char *atoms[] = {
#define DEF(a, b) b,
/* keywords */
DEF(null, "null") /* must be first */
DEF(false, "false")
DEF(true, "true")
DEF(if, "if")
DEF(else, "else")
DEF(return, "return")
DEF(var, "var")
DEF(this, "this")
DEF(delete, "delete")
DEF(void, "void")
DEF(typeof, "typeof")
DEF(new, "new")
DEF(in, "in")
DEF(instanceof, "instanceof")
DEF(do, "do")
DEF(while, "while")
DEF(for, "for")
DEF(break, "break")
DEF(continue, "continue")
DEF(switch, "switch")
DEF(case, "case")
DEF(default, "default")
DEF(throw, "throw")
DEF(try, "try")
DEF(catch, "catch")
DEF(finally, "finally")
DEF(function, "function")
DEF(debugger, "debugger")
DEF(with, "with")
/* FutureReservedWord */
DEF(class, "class")
DEF(const, "const")
DEF(enum, "enum")
DEF(export, "export")
DEF(extends, "extends")
DEF(import, "import")
DEF(super, "super")
/* FutureReservedWords when parsing strict mode code */
DEF(implements, "implements")
DEF(interface, "interface")
DEF(let, "let")
DEF(package, "package")
DEF(private, "private")
DEF(protected, "protected")
DEF(public, "public")
DEF(static, "static")
DEF(yield, "yield")
#undef DEF
/* other atoms */
"",
"toString",
"valueOf",
"number",
"object",
"undefined",
"string",
"boolean",
"<ret>",
"<eval>",
"eval",
"arguments",
"value",
"get",
"set",
"prototype",
"constructor",
"length",
"target",
"of",
"NaN",
"Infinity",
"-Infinity",
"name",
"Error",
"__proto__",
"index",
"input",
};
static char *cvt_name(char *buf, size_t buf_size, const char *str)
{
size_t i, len = strlen(str);
assert(len < buf_size);
if (len == 0) {
strcpy(buf, "empty");
} else {
strcpy(buf, str);
for(i = 0; i < len; i++) {
if (buf[i] == '<' || buf[i] == '>' || buf[i] == '-')
buf[i] = '_';
}
}
return buf;
}
static BOOL is_ascii_string(const char *buf, size_t len)
{
size_t i;
for(i = 0; i < len; i++) {
if ((uint8_t)buf[i] > 0x7f)
return FALSE;
}
return TRUE;
}
static BOOL is_numeric_string(const char *buf, size_t len)
{
return (!strcmp(buf, "NaN") ||
!strcmp(buf, "Infinity") ||
!strcmp(buf, "-Infinity"));
}
static int find_atom(AtomList *s, const char *str)
{
int i;
for(i = 0; i < s->count; i++) {
if (!strcmp(str, s->tab[i].str))
return i;
}
return -1;
}
static int add_atom(AtomList *s, const char *str)
{
int i;
AtomDef *e;
i = find_atom(s, str);
if (i >= 0)
return s->tab[i].offset;
if ((s->count + 1) > s->size) {
s->size = max_int(s->count + 1, s->size * 3 / 2);
s->tab = realloc(s->tab, sizeof(s->tab[0]) * s->size);
}
e = &s->tab[s->count++];
e->str = strdup(str);
e->offset = s->offset;
s->offset += 1 + ((strlen(str) + JSW) / JSW);
return s->count - 1;
}
static int add_cfunc(CFuncList *s, const char *name, int length, const char *magic, const char *cproto_name, const char *cfunc_name)
{
int i;
CFuncDef *e;
for(i = 0; i < s->count; i++) {
e = &s->tab[i];
if (!strcmp(name, e->name) &&
length == e->length &&
!strcmp(magic, e->magic) &&
!strcmp(cproto_name, e->cproto_name) &&
!strcmp(cfunc_name, e->cfunc_name)) {
return i;
}
}
if ((s->count + 1) > s->size) {
s->size = max_int(s->count + 1, s->size * 3 / 2);
s->tab = realloc(s->tab, sizeof(s->tab[0]) * s->size);
}
e = &s->tab[s->count++];
e->name = strdup(name);
e->magic = strdup(magic);
e->length = length;
e->cproto_name = strdup(cproto_name);
e->cfunc_name = strdup(cfunc_name);
return s->count - 1;
}
static void dump_atom_defines(void)
{
AtomList atom_list_s, *s = &atom_list_s;
AtomDef *e;
int i;
char buf[256];
memset(s, 0, sizeof(*s));
/* add the predefined atoms (they have a corresponding define) */
for(i = 0; i < countof(atoms); i++) {
add_atom(s, atoms[i]);
}
for(i = 0; i < s->count; i++) {
e = &s->tab[i];
printf("#define JS_ATOM_%s %d\n",
cvt_name(buf, sizeof(buf), e->str), e->offset);
}
printf("\n");
printf("#define JS_ATOM_END %d\n", s->offset);
printf("\n");
}
static int atom_cmp(const void *p1, const void *p2)
{
const AtomDef *a1 = (const AtomDef *)p1;
const AtomDef *a2 = (const AtomDef *)p2;
return strcmp(a1->str, a2->str);
}
/* js_atom_table must be properly aligned because the property hash
table uses the low bits of the atom pointer value */
#define ATOM_ALIGN 64
static void dump_atoms(BuildContext *ctx)
{
AtomList *s = &ctx->atom_list;
int i, j, k, l, len, len1, is_ascii, is_numeric;
uint64_t v;
const char *str;
AtomDef *sorted_atoms;
char buf[256];
sorted_atoms = malloc(sizeof(sorted_atoms[0]) * s->count);
memcpy(sorted_atoms, s->tab, sizeof(sorted_atoms[0]) * s->count);
qsort(sorted_atoms, s->count, sizeof(sorted_atoms[0]), atom_cmp);
printf(" /* atom_table */\n");
for(i = 0; i < s->count; i++) {
str = s->tab[i].str;
len = strlen(str);
is_ascii = is_ascii_string(str, len);
is_numeric = is_numeric_string(str, len);
printf(" (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (%d << (JS_MTAG_BITS + 1)) | (%d << (JS_MTAG_BITS + 2)) | (%d << (JS_MTAG_BITS + 3)), /* \"%s\" (offset=%d) */\n",
is_ascii, is_numeric, len, str, ctx->cur_offset);
len1 = (len + JSW) / JSW;
for(j = 0; j < len1; j++) {
l = min_uint32(JSW, len - j * JSW);
v = 0;
for(k = 0; k < l; k++)
v |= (uint64_t)(uint8_t)str[j * JSW + k] << (k * 8);
printf(" 0x%0*" PRIx64 ",\n", JSW * 2, v);
}
assert(ctx->cur_offset == s->tab[i].offset);
ctx->cur_offset += len1 + 1;
}
printf("\n");
ctx->sorted_atom_table_offset = ctx->cur_offset;
printf(" /* sorted atom table (offset=%d) */\n", ctx->cur_offset);
printf(" JS_VALUE_ARRAY_HEADER(%d),\n", s->count);
for(i = 0; i < s->count; i++) {
AtomDef *e = &sorted_atoms[i];
printf(" JS_ROM_VALUE(%d), /* %s */\n",
e->offset, cvt_name(buf, sizeof(buf), e->str));
}
ctx->cur_offset += s->count + 1;
printf("\n");
free(sorted_atoms);
}
static int define_value(BuildContext *s, const JSPropDef *d);
static uint32_t dump_atom(BuildContext *s, const char *str, BOOL value_only)
{
int len, idx, i, offset;
len = strlen(str);
for(i = 0; i < len; i++) {
if ((uint8_t)str[i] >= 128) {
fprintf(stderr, "unicode property names are not supported yet (%s)\n", str);
exit(1);
}
}
if (len >= 1 && (str[0] >= '0' && str[0] <= '9')) {
fprintf(stderr, "numeric property names are not supported yet (%s)\n", str);
exit(1);
}
if (len == 1) {
if (value_only) {
/* XXX: hardcoded */
return ((uint8_t)str[0] << 5) | 0x1b;
}
printf("JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, %d)",
(uint8_t)str[0]);
} else {
idx = find_atom(&s->atom_list, str);
if (idx < 0) {
fprintf(stderr, "atom '%s' is undefined\n", str);
exit(1);
}
offset = s->atom_list.tab[idx].offset;
if (value_only)
return (offset * JSW) + 1; /* correct modulo ATOM_ALIGN */
printf("JS_ROM_VALUE(%d)", offset);
}
printf(" /* %s */", str);
return 0;
}
static void dump_cfuncs(BuildContext *s)
{
int i;
CFuncDef *e;
printf("static const JSCFunctionDef js_c_function_table[] = {\n");
for(i = 0; i < s->cfunc_list.count; i++) {
e = &s->cfunc_list.tab[i];
printf(" { { .%s = %s },\n", e->cproto_name, e->cfunc_name);
printf(" ");
dump_atom(s, e->name, FALSE);
printf(",\n");
printf(" JS_CFUNC_%s, %d, %s },\n",
e->cproto_name, e->length, e->magic);
}
printf("};\n\n");
}
static void dump_cfinalizers(BuildContext *s)
{
struct list_head *el;
ClassDefEntry *e;
printf("static const JSCFinalizer js_c_finalizer_table[JS_CLASS_COUNT - JS_CLASS_USER] = {\n");
list_for_each(el, &s->class_list) {
e = list_entry(el, ClassDefEntry, link);
if (e->finalizer_name &&
strcmp(e->finalizer_name, "NULL") != 0) {
printf(" [%s - JS_CLASS_USER] = %s,\n", e->class_id, e->finalizer_name);
}
}
printf("};\n\n");
}
typedef enum {
PROPS_KIND_GLOBAL,
PROPS_KIND_PROTO,
PROPS_KIND_CLASS,
PROPS_KIND_OBJECT,
} JSPropsKindEnum;
static inline uint32_t hash_prop(BuildContext *s, const char *name)
{
/* Compute the hash for a symbol, must be consistent with
mquickjs.c implementation.
*/
uint32_t prop = dump_atom(s, name, TRUE);
return (prop / JSW) ^ (prop % JSW); /* XXX: improve */
}
static int define_props(BuildContext *s, const JSPropDef *props_def,
JSPropsKindEnum props_kind, const char *class_id_str)
{
int i, *ident_tab, idx, props_ident, n_props;
int prop_idx;
const JSPropDef *d;
uint32_t *prop_hash;
BOOL is_global_object = (props_kind == PROPS_KIND_GLOBAL);
static const JSPropDef dummy_props[] = {
{ JS_DEF_END },
};
if (!props_def)
props_def = dummy_props;
n_props = 0;
for(d = props_def; d->def_type != JS_DEF_END; d++) {
n_props++;
}
if (props_kind == PROPS_KIND_PROTO ||
props_kind == PROPS_KIND_CLASS)
n_props++;
ident_tab = malloc(sizeof(ident_tab[0]) * n_props);
/* define the various objects */
for(d = props_def, i = 0; d->def_type != JS_DEF_END; d++, i++) {
ident_tab[i] = define_value(s, d);
}
props_ident = -1;
prop_hash = NULL;
if (is_global_object) {
props_ident = s->cur_offset;
printf(" /* global object properties (offset=%d) */\n", props_ident);
printf(" JS_VALUE_ARRAY_HEADER(%d),\n", 2 * n_props);
s->cur_offset += 2 * n_props + 1;
} else {
int hash_size_log2;
uint32_t hash_size, hash_mask;
uint32_t *hash_table, h;
if (n_props <= 1)
hash_size_log2 = 0;
else
hash_size_log2 = (32 - clz32(n_props - 1)) - 1;
hash_size = 1 << hash_size_log2;
if (hash_size > ATOM_ALIGN / JSW) {
#if !defined __APPLE__
// XXX: Cannot request data alignment larger than 64 bytes on Darwin
fprintf(stderr, "Too many properties, consider increasing ATOM_ALIGN\n");
#endif
hash_size = ATOM_ALIGN / JSW;
}
hash_mask = hash_size - 1;
hash_table = malloc(sizeof(hash_table[0]) * hash_size);
prop_hash = malloc(sizeof(prop_hash[0]) * n_props);
/* build the hash table */
for(i = 0; i < hash_size; i++)
hash_table[i] = 0;
prop_idx = 0;
for(i = 0, d = props_def; i < n_props; i++, d++) {
const char *name;
if (d->def_type != JS_DEF_END) {
name = d->name;
} else {
if (props_kind == PROPS_KIND_PROTO)
name = "constructor";
else
name = "prototype";
}
h = hash_prop(s, name) & hash_mask;
prop_hash[prop_idx] = hash_table[h];
hash_table[h] = 2 + hash_size + 3 * prop_idx;
prop_idx++;
}
props_ident = s->cur_offset;
printf(" /* properties (offset=%d) */\n", props_ident);
printf(" JS_VALUE_ARRAY_HEADER(%d),\n", 2 + hash_size + n_props * 3);
printf(" %d << 1, /* n_props */\n", n_props);
printf(" %d << 1, /* hash_mask */\n", hash_mask);
for(i = 0; i < hash_size; i++) {
printf(" %d << 1,\n", hash_table[i]);
}
s->cur_offset += hash_size + 3 + 3 * n_props;
free(hash_table);
}
prop_idx = 0;
for(d = props_def, i = 0; i < n_props; d++, i++) {
const char *name, *prop_type;
/* name */
printf(" ");
if (d->def_type != JS_DEF_END) {
name = d->name;
} else {
if (props_kind == PROPS_KIND_PROTO)
name = "constructor";
else
name = "prototype";
}
dump_atom(s, name, FALSE);
printf(",\n");
printf(" ");
prop_type = "NORMAL";
switch(d->def_type) {
case JS_DEF_PROP_DOUBLE:
if (ident_tab[i] >= 0)
goto value_ptr;
/* short int */
printf("%d << 1,", (int32_t)d->u.f64);
break;
case JS_DEF_CGETSET:
if (is_global_object) {
fprintf(stderr, "getter/setter forbidden in global object\n");
exit(1);
}
prop_type = "GETSET";
goto value_ptr;
case JS_DEF_CLASS:
value_ptr:
assert(ident_tab[i] >= 0);
printf("JS_ROM_VALUE(%d),", ident_tab[i]);
break;
case JS_DEF_PROP_UNDEFINED:
printf("JS_UNDEFINED,");
break;
case JS_DEF_PROP_NULL:
printf("JS_NULL,");
break;
case JS_DEF_PROP_STRING:
dump_atom(s, d->u.str, FALSE);
printf(",");
break;
case JS_DEF_CFUNC:
idx = add_cfunc(&s->cfunc_list,
d->name,
d->u.func.length,
d->u.func.magic,
d->u.func.cproto_name,
d->u.func.func_name);
printf("JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),", idx);
break;
case JS_DEF_END:
if (props_kind == PROPS_KIND_PROTO) {
/* constructor property */
printf("(uint32_t)(-%s - 1) << 1,", class_id_str);
} else {
/* prototype property */
printf("%s << 1,", class_id_str);
}
prop_type = "SPECIAL";
break;
default:
abort();
}
printf("\n");
if (!is_global_object) {
printf(" (%d << 1) | (JS_PROP_%s << 30),\n",
prop_hash[prop_idx], prop_type);
}
prop_idx++;
}
free(prop_hash);
free(ident_tab);
return props_ident;
}
static ClassDefEntry *find_class(BuildContext *s, const JSClassDef *d)
{
struct list_head *el;
ClassDefEntry *e;
list_for_each(el, &s->class_list) {
e = list_entry(el, ClassDefEntry, link);
if (e->class1 == d)
return e;
}
return NULL;
}
static void free_class_entries(BuildContext *s)
{
struct list_head *el, *el1;
ClassDefEntry *e;
list_for_each_safe(el, el1, &s->class_list) {
e = list_entry(el, ClassDefEntry, link);
free(e->class_id);
free(e->finalizer_name);
free(e);
}
init_list_head(&s->class_list);
}
static int define_class(BuildContext *s, const JSClassDef *d)
{
int ctor_func_idx = -1, class_props_idx = -1, proto_props_idx = -1;
int ident, parent_class_idx = -1;
ClassDefEntry *e;
/* check if the class is already defined */
e = find_class(s, d);
if (e)
return e->class_idx;
if (d->parent_class)
parent_class_idx = define_class(s, d->parent_class);
if (d->func_name) {
ctor_func_idx = add_cfunc(&s->cfunc_list,
d->name,
d->length,
d->class_id,
d->cproto_name,
d->func_name);
}
if (ctor_func_idx >= 0) {
class_props_idx = define_props(s, d->class_props, PROPS_KIND_CLASS, d->class_id);
proto_props_idx = define_props(s, d->proto_props, PROPS_KIND_PROTO, d->class_id);
} else {
if (d->class_props)
class_props_idx = define_props(s, d->class_props, PROPS_KIND_OBJECT, d->class_id);
}
ident = s->cur_offset;
printf(" /* class (offset=%d) */\n", ident);
printf(" JS_MB_HEADER_DEF(JS_MTAG_OBJECT),\n");
if (class_props_idx >= 0)
printf(" JS_ROM_VALUE(%d),\n", class_props_idx);
else
printf(" JS_NULL,\n");
printf(" %d,\n", ctor_func_idx);
if (proto_props_idx >= 0)
printf(" JS_ROM_VALUE(%d),\n", proto_props_idx);
else
printf(" JS_NULL,\n");
if (parent_class_idx >= 0) {
printf(" JS_ROM_VALUE(%d),\n", parent_class_idx);
} else {
printf(" JS_NULL,\n");
}
printf("\n");
s->cur_offset += 5;
e = malloc(sizeof(*e));
memset(e, 0, sizeof(*e));
e->class_idx = ident;
e->class1 = d;
if (ctor_func_idx >= 0) {
e->class_id = strdup(d->class_id);
e->finalizer_name = strdup(d->finalizer_name);
}
list_add_tail(&e->link, &s->class_list);
return ident;
}
#define JS_SHORTINT_MIN (-(1 << 30))
#define JS_SHORTINT_MAX ((1 << 30) - 1)
static BOOL is_short_int(double d)
{
return (d >= JS_SHORTINT_MIN && d <= JS_SHORTINT_MAX && (int32_t)d == d);
}
static int define_value(BuildContext *s, const JSPropDef *d)
{
int ident;
ident = -1;
switch(d->def_type) {
case JS_DEF_PROP_DOUBLE:
{
uint64_t v;
if (!is_short_int(d->u.f64)) {
ident = s->cur_offset;
printf(" /* float64 (offset=%d) */\n", ident);
printf(" JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),\n");
v = float64_as_uint64(d->u.f64);
if (JSW == 8) {
printf(" 0x%016zx,\n", (size_t)v);
printf("\n");
s->cur_offset += 2;
} else {
/* XXX: little endian assumed */
printf(" 0x%08x,\n", (uint32_t)v);
printf(" 0x%08x,\n", (uint32_t)(v >> 32));
printf("\n");
s->cur_offset += 3;
}
}
}
break;
case JS_DEF_CLASS:
ident = define_class(s, d->u.class1);
break;
case JS_DEF_CGETSET:
{
int get_idx = -1, set_idx = -1;
char buf[256];
if (strcmp(d->u.getset.get_func_name, "NULL") != 0) {
snprintf(buf, sizeof(buf), "get %s", d->name);
get_idx = add_cfunc(&s->cfunc_list,
buf,
0, /* length */
d->u.getset.magic,
d->u.getset.cproto_name,
d->u.getset.get_func_name);
}
if (strcmp(d->u.getset.set_func_name, "NULL") != 0) {
snprintf(buf, sizeof(buf), "set %s", d->name);
set_idx = add_cfunc(&s->cfunc_list,
buf,
1, /* length */
d->u.getset.magic,
d->u.getset.cproto_name,
d->u.getset.set_func_name);
}
ident = s->cur_offset;
printf(" /* getset (offset=%d) */\n", ident);
printf(" JS_VALUE_ARRAY_HEADER(2),\n");
if (get_idx >= 0)
printf(" JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),\n", get_idx);
else
printf(" JS_UNDEFINED,\n");
if (set_idx >= 0)
printf(" JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),\n", set_idx);
else
printf(" JS_UNDEFINED,\n");
printf("\n");
s->cur_offset += 3;
}
break;
default:
break;
}
return ident;
}
static void define_atoms_props(BuildContext *s, const JSPropDef *props_def, JSPropsKindEnum props_kind);
static void define_atoms_class(BuildContext *s, const JSClassDef *d)
{
ClassDefEntry *e;
/* check if the class is already defined */
e = find_class(s, d);
if (e)
return;
if (d->parent_class)
define_atoms_class(s, d->parent_class);
if (d->func_name)
add_atom(&s->atom_list, d->name);
if (d->class_props)
define_atoms_props(s, d->class_props, d->func_name ? PROPS_KIND_CLASS : PROPS_KIND_OBJECT);
if (d->proto_props)
define_atoms_props(s, d->proto_props, PROPS_KIND_PROTO);
}
static void define_atoms_props(BuildContext *s, const JSPropDef *props_def, JSPropsKindEnum props_kind)
{
const JSPropDef *d;
for(d = props_def; d->def_type != JS_DEF_END; d++) {
add_atom(&s->atom_list, d->name);
switch(d->def_type) {
case JS_DEF_PROP_STRING:
add_atom(&s->atom_list, d->u.str);
break;
case JS_DEF_CLASS:
define_atoms_class(s, d->u.class1);
break;
case JS_DEF_CGETSET:
{
char buf[256];
if (strcmp(d->u.getset.get_func_name, "NULL") != 0) {
snprintf(buf, sizeof(buf), "get %s", d->name);
add_atom(&s->atom_list, buf);
}
if (strcmp(d->u.getset.set_func_name, "NULL") != 0) {
snprintf(buf, sizeof(buf), "set %s", d->name);
add_atom(&s->atom_list, buf);
}
}
break;
default:
break;
}
}
}
static int usage(const char *name)
{
fprintf(stderr, "usage: %s {-m32 | -m64} [-a]\n", name);
fprintf(stderr,
" create a ROM file for the mquickjs standard library\n"
"--help list options\n"
"-m32 force generation for a 32 bit target\n"
"-m64 force generation for a 64 bit target\n"
"-a generate the mquickjs_atom.h header\n"
);
return 1;
}
int build_atoms(const char *stdlib_name, const JSPropDef *global_obj,
const JSPropDef *c_function_decl, int argc, char **argv)
{
int i;
unsigned jsw;
BuildContext ss, *s = &ss;
BOOL build_atom_defines = FALSE;
#if INTPTR_MAX >= INT64_MAX
jsw = 8;
#else
jsw = 4;
#endif
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-m64")) {
jsw = 8;
} else if (!strcmp(argv[i], "-m32")) {
jsw = 4;
} else if (!strcmp(argv[i], "-a")) {
build_atom_defines = TRUE;
} else if (!strcmp(argv[i], "--help")) {
return usage(argv[0]);
} else {
fprintf(stderr, "invalid argument '%s'\n", argv[i]);
return usage(argv[0]);
}
}
JSW = jsw;
if (build_atom_defines) {
dump_atom_defines();
return 0;
}
memset(s, 0, sizeof(*s));
init_list_head(&s->class_list);
/* add the predefined atoms (they have a corresponding define) */
for(i = 0; i < countof(atoms); i++) {
add_atom(&s->atom_list, atoms[i]);
}
/* add the predefined functions */
if (c_function_decl) {
const JSPropDef *d;
for(d = c_function_decl; d->def_type != JS_DEF_END; d++) {
if (d->def_type != JS_DEF_CFUNC) {
fprintf(stderr, "only C functions are allowed in c_function_decl[]\n");
exit(1);
}
add_atom(&s->atom_list, d->name);
add_cfunc(&s->cfunc_list,
d->name,
d->u.func.length,
d->u.func.magic,
d->u.func.cproto_name,
d->u.func.func_name);
}
}
/* first pass to define the atoms */
define_atoms_props(s, global_obj, PROPS_KIND_GLOBAL);
free_class_entries(s);
printf("/* this file is automatically generated - do not edit */\n\n");
printf("#include \"mquickjs_priv.h\"\n\n");
printf("static const uint%u_t __attribute((aligned(%d))) js_stdlib_table[] = {\n",
JSW * 8, ATOM_ALIGN);
dump_atoms(s);
s->global_object_offset = define_props(s, global_obj, PROPS_KIND_GLOBAL, NULL);
printf("};\n\n");
dump_cfuncs(s);
printf("#ifndef JS_CLASS_COUNT\n"
"#define JS_CLASS_COUNT JS_CLASS_USER /* total number of classes */\n"
"#endif\n\n");
dump_cfinalizers(s);
free_class_entries(s);
printf("const JSSTDLibraryDef %s = {\n", stdlib_name);
printf(" js_stdlib_table,\n");
printf(" js_c_function_table,\n");
printf(" js_c_finalizer_table,\n");
printf(" %d,\n", s->cur_offset);
printf(" %d,\n", ATOM_ALIGN);
printf(" %d,\n", s->sorted_atom_table_offset);
printf(" %d,\n", s->global_object_offset);
printf(" JS_CLASS_COUNT,\n");
printf("};\n\n");
return 0;
}

View File

@ -0,0 +1,97 @@
/*
* Micro QuickJS build utility
*
* Copyright (c) 2017-2025 Fabrice Bellard
* Copyright (c) 2017-2025 Charlie Gordon
*
* 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 MQUICKJS_BUILD_H
#define MQUICKJS_BUILD_H
#include <stdlib.h>
#include <inttypes.h>
enum {
JS_DEF_END,
JS_DEF_CFUNC,
JS_DEF_CGETSET,
JS_DEF_PROP_DOUBLE,
JS_DEF_PROP_UNDEFINED,
JS_DEF_PROP_STRING,
JS_DEF_PROP_NULL,
JS_DEF_CLASS,
};
typedef struct JSClassDef JSClassDef;
typedef struct JSPropDef {
int def_type;
const char *name;
union {
struct {
uint8_t length;
const char *magic;
const char *cproto_name;
const char *func_name;
} func;
struct {
const char *magic;
const char *cproto_name;
const char *get_func_name;
const char *set_func_name;
} getset;
double f64;
const JSClassDef *class1;
const char *str;
} u;
} JSPropDef;
typedef struct JSClassDef {
const char *name;
int length;
const char *cproto_name;
const char *func_name;
const char *class_id;
const JSPropDef *class_props; /* NULL if none */
const JSPropDef *proto_props; /* NULL if none */
const JSClassDef *parent_class; /* NULL if none */
const char *finalizer_name; /* "NULL" if none */
} JSClassDef;
#define JS_PROP_END { JS_DEF_END }
#define JS_CFUNC_DEF(name, length, func_name) { JS_DEF_CFUNC, name, { .func = { length, "0", "generic", #func_name } } }
#define JS_CFUNC_MAGIC_DEF(name, length, func_name, magic) { JS_DEF_CFUNC, name, { .func = { length, #magic, "generic_magic", #func_name } } }
#define JS_CFUNC_SPECIAL_DEF(name, length, proto, func_name) { JS_DEF_CFUNC, name, { .func = { length, "0", #proto, #func_name } } }
#define JS_CGETSET_DEF(name, get_name, set_name) { JS_DEF_CGETSET, name, { .getset = { "0", "generic", #get_name, #set_name } } }
#define JS_CGETSET_MAGIC_DEF(name, get_name, set_name, magic) { JS_DEF_CGETSET, name, { .getset = { #magic, "generic_magic", #get_name, #set_name } } }
#define JS_PROP_CLASS_DEF(name, cl) { JS_DEF_CLASS, name, { .class1 = cl } }
#define JS_PROP_DOUBLE_DEF(name, val, flags) { JS_DEF_PROP_DOUBLE, name, { .f64 = val } }
#define JS_PROP_UNDEFINED_DEF(name, flags) { JS_DEF_PROP_UNDEFINED, name }
#define JS_PROP_NULL_DEF(name, flags) { JS_DEF_PROP_NULL, name }
#define JS_PROP_STRING_DEF(name, cstr, flags) { JS_DEF_PROP_STRING, name, { .str = cstr } }
#define JS_CLASS_DEF(name, length, func_name, class_id, class_props, proto_props, parent_class, finalizer_name) { name, length, "constructor", #func_name, #class_id, class_props, proto_props, parent_class, #finalizer_name }
#define JS_CLASS_MAGIC_DEF(name, length, func_name, class_id, class_props, proto_props, parent_class, finalizer_name) { name, length, "constructor_magic", #func_name, #class_id, class_props, proto_props, parent_class, #finalizer_name }
#define JS_OBJECT_DEF(name, obj_props) { name, 0, NULL, NULL, NULL, obj_props, NULL, NULL, NULL }
int build_atoms(const char *stdlib_name, const JSPropDef *global_obj,
const JSPropDef *c_function_decl, int argc, char **argv);
#endif /* MQUICKJS_BUILD_H */

View File

@ -0,0 +1,970 @@
/*
* SoftFP Library
*
* Copyright (c) 2016 Fabrice Bellard
*
* 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.
*/
#if F_SIZE == 32
#define F_UINT uint32_t
#define F_ULONG uint64_t
#define MANT_SIZE 23
#define EXP_SIZE 8
#elif F_SIZE == 64
#define F_UHALF uint32_t
#define F_UINT uint64_t
#ifdef HAVE_INT128
#define F_ULONG uint128_t
#endif
#define MANT_SIZE 52
#define EXP_SIZE 11
#elif F_SIZE == 128
#define F_UHALF uint64_t
#define F_UINT uint128_t
#define MANT_SIZE 112
#define EXP_SIZE 15
#else
#error unsupported F_SIZE
#endif
#define EXP_MASK ((1 << EXP_SIZE) - 1)
#define MANT_MASK (((F_UINT)1 << MANT_SIZE) - 1)
#define SIGN_MASK ((F_UINT)1 << (F_SIZE - 1))
#define IMANT_SIZE (F_SIZE - 2) /* internal mantissa size */
#define RND_SIZE (IMANT_SIZE - MANT_SIZE)
#define QNAN_MASK ((F_UINT)1 << (MANT_SIZE - 1))
#define EXP_BIAS ((1 << (EXP_SIZE - 1)) - 1)
/* quiet NaN */
#define F_QNAN glue(F_QNAN, F_SIZE)
#define clz glue(clz, F_SIZE)
#define pack_sf glue(pack_sf, F_SIZE)
#define unpack_sf glue(unpack_sf, F_SIZE)
#define rshift_rnd glue(rshift_rnd, F_SIZE)
#define round_pack_sf glue(roundpack_sf, F_SIZE)
#define normalize_sf glue(normalize_sf, F_SIZE)
#define normalize2_sf glue(normalize2_sf, F_SIZE)
#define issignan_sf glue(issignan_sf, F_SIZE)
#define isnan_sf glue(isnan_sf, F_SIZE)
#define add_sf glue(add_sf, F_SIZE)
#define mul_sf glue(mul_sf, F_SIZE)
#define fma_sf glue(fma_sf, F_SIZE)
#define div_sf glue(div_sf, F_SIZE)
#define sqrt_sf glue(sqrt_sf, F_SIZE)
#define normalize_subnormal_sf glue(normalize_subnormal_sf, F_SIZE)
#define divrem_u glue(divrem_u, F_SIZE)
#define sqrtrem_u glue(sqrtrem_u, F_SIZE)
#define mul_u glue(mul_u, F_SIZE)
#define cvt_sf32_sf glue(cvt_sf32_sf, F_SIZE)
#define cvt_sf64_sf glue(cvt_sf64_sf, F_SIZE)
static const F_UINT F_QNAN = (((F_UINT)EXP_MASK << MANT_SIZE) | ((F_UINT)1 << (MANT_SIZE - 1)));
static inline F_UINT pack_sf(uint32_t a_sign, uint32_t a_exp, F_UINT a_mant)
{
return ((F_UINT)a_sign << (F_SIZE - 1)) |
((F_UINT)a_exp << MANT_SIZE) |
(a_mant & MANT_MASK);
}
static inline F_UINT unpack_sf(uint32_t *pa_sign, int32_t *pa_exp,
F_UINT a)
{
*pa_sign = a >> (F_SIZE - 1);
*pa_exp = (a >> MANT_SIZE) & EXP_MASK;
return a & MANT_MASK;
}
static F_UINT rshift_rnd(F_UINT a, int d)
{
F_UINT mask;
if (d != 0) {
if (d >= F_SIZE) {
a = (a != 0);
} else {
mask = ((F_UINT)1 << d) - 1;
a = (a >> d) | ((a & mask) != 0);
}
}
return a;
}
#if F_USE_FFLAGS
#define FFLAGS_PARAM , uint32_t *pfflags
#define FFLAGS_ARG , pfflags
#else
#define FFLAGS_PARAM
#define FFLAGS_ARG
#endif
/* a_mant is considered to have its MSB at F_SIZE - 2 bits */
static F_UINT round_pack_sf(uint32_t a_sign, int a_exp, F_UINT a_mant,
RoundingModeEnum rm FFLAGS_PARAM)
{
int diff;
uint32_t addend, rnd_bits;
switch(rm) {
case RM_RNE:
case RM_RMM:
addend = (1 << (RND_SIZE - 1));
break;
case RM_RTZ:
addend = 0;
break;
default:
case RM_RDN:
case RM_RUP:
// printf("s=%d rm=%d m=%x\n", a_sign, rm, a_mant);
if (a_sign ^ (rm & 1))
addend = (1 << RND_SIZE) - 1;
else
addend = 0;
break;
}
/* potentially subnormal */
if (a_exp <= 0) {
BOOL is_subnormal;
/* Note: we set the underflow flag if the rounded result
is subnormal and inexact */
is_subnormal = (a_exp < 0 ||
(a_mant + addend) < ((F_UINT)1 << (F_SIZE - 1)));
diff = 1 - a_exp;
a_mant = rshift_rnd(a_mant, diff);
rnd_bits = a_mant & ((1 << RND_SIZE ) - 1);
if (is_subnormal && rnd_bits != 0) {
#if F_USE_FFLAGS
*pfflags |= FFLAG_UNDERFLOW;
#endif
}
a_exp = 1;
} else {
rnd_bits = a_mant & ((1 << RND_SIZE ) - 1);
}
#if F_USE_FFLAGS
if (rnd_bits != 0)
*pfflags |= FFLAG_INEXACT;
#endif
a_mant = (a_mant + addend) >> RND_SIZE;
/* half way: select even result */
if (rm == RM_RNE && rnd_bits == (1 << (RND_SIZE - 1)))
a_mant &= ~1;
/* Note the rounding adds at least 1, so this is the maximum
value */
a_exp += a_mant >> (MANT_SIZE + 1);
if (a_mant <= MANT_MASK) {
/* denormalized or zero */
a_exp = 0;
} else if (a_exp >= EXP_MASK) {
/* overflow */
if (addend == 0) {
a_exp = EXP_MASK - 1;
a_mant = MANT_MASK;
} else {
/* infinity */
a_exp = EXP_MASK;
a_mant = 0;
}
#if F_USE_FFLAGS
*pfflags |= FFLAG_OVERFLOW | FFLAG_INEXACT;
#endif
}
return pack_sf(a_sign, a_exp, a_mant);
}
/* a_mant is considered to have at most F_SIZE - 1 bits */
static F_UINT normalize_sf(uint32_t a_sign, int a_exp, F_UINT a_mant,
RoundingModeEnum rm FFLAGS_PARAM)
{
int shift;
shift = clz(a_mant) - (F_SIZE - 1 - IMANT_SIZE);
assert(shift >= 0);
a_exp -= shift;
a_mant <<= shift;
return round_pack_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG);
}
static inline F_UINT normalize_subnormal_sf(int32_t *pa_exp, F_UINT a_mant)
{
int shift;
shift = MANT_SIZE - ((F_SIZE - 1 - clz(a_mant)));
*pa_exp = 1 - shift;
return a_mant << shift;
}
#if F_USE_FFLAGS
F_STATIC BOOL issignan_sf(F_UINT a)
{
uint32_t a_exp1;
F_UINT a_mant;
a_exp1 = (a >> (MANT_SIZE - 1)) & ((1 << (EXP_SIZE + 1)) - 1);
a_mant = a & MANT_MASK;
return (a_exp1 == (2 * EXP_MASK) && a_mant != 0);
}
#endif
#ifndef F_NORMALIZE_ONLY
F_STATIC BOOL isnan_sf(F_UINT a)
{
uint32_t a_exp;
F_UINT a_mant;
a_exp = (a >> MANT_SIZE) & EXP_MASK;
a_mant = a & MANT_MASK;
return (a_exp == EXP_MASK && a_mant != 0);
}
F_STATIC F_UINT add_sf(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM)
{
uint32_t a_sign, b_sign, a_exp, b_exp;
F_UINT tmp, a_mant, b_mant;
/* swap so that abs(a) >= abs(b) */
if ((a & ~SIGN_MASK) < (b & ~SIGN_MASK)) {
tmp = a;
a = b;
b = tmp;
}
a_sign = a >> (F_SIZE - 1);
b_sign = b >> (F_SIZE - 1);
a_exp = (a >> MANT_SIZE) & EXP_MASK;
b_exp = (b >> MANT_SIZE) & EXP_MASK;
a_mant = (a & MANT_MASK) << 3;
b_mant = (b & MANT_MASK) << 3;
if (unlikely(a_exp == EXP_MASK)) {
if (a_mant != 0) {
/* NaN result */
#if F_USE_FFLAGS
if (!(a_mant & (QNAN_MASK << 3)) || issignan_sf(b))
*pfflags |= FFLAG_INVALID_OP;
#endif
return F_QNAN;
} else if (b_exp == EXP_MASK && a_sign != b_sign) {
#if F_USE_FFLAGS
*pfflags |= FFLAG_INVALID_OP;
#endif
return F_QNAN;
} else {
/* infinity */
return a;
}
}
if (a_exp == 0) {
a_exp = 1;
} else {
a_mant |= (F_UINT)1 << (MANT_SIZE + 3);
}
if (b_exp == 0) {
b_exp = 1;
} else {
b_mant |= (F_UINT)1 << (MANT_SIZE + 3);
}
b_mant = rshift_rnd(b_mant, a_exp - b_exp);
if (a_sign == b_sign) {
/* same signs : add the absolute values */
a_mant += b_mant;
} else {
/* different signs : subtract the absolute values */
a_mant -= b_mant;
if (a_mant == 0) {
/* zero result : the sign needs a specific handling */
a_sign = (rm == RM_RDN);
}
}
a_exp += (RND_SIZE - 3);
return normalize_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG);
}
F_STATIC F_UINT glue(sub_sf, F_SIZE)(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM)
{
return add_sf(a, b ^ SIGN_MASK, rm FFLAGS_ARG);
}
#ifdef F_ULONG
static F_UINT mul_u(F_UINT *plow, F_UINT a, F_UINT b)
{
F_ULONG r;
r = (F_ULONG)a * (F_ULONG)b;
*plow = r;
return r >> F_SIZE;
}
#else
#define FH_SIZE (F_SIZE / 2)
static F_UINT mul_u(F_UINT *plow, F_UINT a, F_UINT b)
{
F_UHALF a0, a1, b0, b1, r0, r1, r2, r3;
F_UINT r00, r01, r10, r11, c;
a0 = a;
a1 = a >> FH_SIZE;
b0 = b;
b1 = b >> FH_SIZE;
r00 = (F_UINT)a0 * (F_UINT)b0;
r01 = (F_UINT)a0 * (F_UINT)b1;
r10 = (F_UINT)a1 * (F_UINT)b0;
r11 = (F_UINT)a1 * (F_UINT)b1;
r0 = r00;
c = (r00 >> FH_SIZE) + (F_UHALF)r01 + (F_UHALF)r10;
r1 = c;
c = (c >> FH_SIZE) + (r01 >> FH_SIZE) + (r10 >> FH_SIZE) + (F_UHALF)r11;
r2 = c;
r3 = (c >> FH_SIZE) + (r11 >> FH_SIZE);
*plow = ((F_UINT)r1 << FH_SIZE) | r0;
return ((F_UINT)r3 << FH_SIZE) | r2;
}
#undef FH_SIZE
#endif
F_STATIC F_UINT mul_sf(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM)
{
uint32_t a_sign, b_sign, r_sign;
int32_t a_exp, b_exp, r_exp;
F_UINT a_mant, b_mant, r_mant, r_mant_low;
a_sign = a >> (F_SIZE - 1);
b_sign = b >> (F_SIZE - 1);
r_sign = a_sign ^ b_sign;
a_exp = (a >> MANT_SIZE) & EXP_MASK;
b_exp = (b >> MANT_SIZE) & EXP_MASK;
a_mant = a & MANT_MASK;
b_mant = b & MANT_MASK;
if (a_exp == EXP_MASK || b_exp == EXP_MASK) {
if (isnan_sf(a) || isnan_sf(b)) {
#if F_USE_FFLAGS
if (issignan_sf(a) || issignan_sf(b)) {
*pfflags |= FFLAG_INVALID_OP;
}
#endif
return F_QNAN;
} else {
/* infinity */
if ((a_exp == EXP_MASK && (b_exp == 0 && b_mant == 0)) ||
(b_exp == EXP_MASK && (a_exp == 0 && a_mant == 0))) {
#if F_USE_FFLAGS
*pfflags |= FFLAG_INVALID_OP;
#endif
return F_QNAN;
} else {
return pack_sf(r_sign, EXP_MASK, 0);
}
}
}
if (a_exp == 0) {
if (a_mant == 0)
return pack_sf(r_sign, 0, 0); /* zero */
a_mant = normalize_subnormal_sf(&a_exp, a_mant);
} else {
a_mant |= (F_UINT)1 << MANT_SIZE;
}
if (b_exp == 0) {
if (b_mant == 0)
return pack_sf(r_sign, 0, 0); /* zero */
b_mant = normalize_subnormal_sf(&b_exp, b_mant);
} else {
b_mant |= (F_UINT)1 << MANT_SIZE;
}
r_exp = a_exp + b_exp - (1 << (EXP_SIZE - 1)) + 2;
r_mant = mul_u(&r_mant_low,a_mant << RND_SIZE, b_mant << (RND_SIZE + 1));
r_mant |= (r_mant_low != 0);
return normalize_sf(r_sign, r_exp, r_mant, rm FFLAGS_ARG);
}
#ifdef F_ULONG
static F_UINT divrem_u(F_UINT *pr, F_UINT ah, F_UINT al, F_UINT b)
{
F_ULONG a;
a = ((F_ULONG)ah << F_SIZE) | al;
*pr = a % b;
return a / b;
}
#else
/* XXX: optimize */
static F_UINT divrem_u(F_UINT *pr, F_UINT a1, F_UINT a0, F_UINT b)
{
int i, qb, ab;
assert(a1 < b);
for(i = 0; i < F_SIZE; i++) {
ab = a1 >> (F_SIZE - 1);
a1 = (a1 << 1) | (a0 >> (F_SIZE - 1));
if (ab || a1 >= b) {
a1 -= b;
qb = 1;
} else {
qb = 0;
}
a0 = (a0 << 1) | qb;
}
*pr = a1;
return a0;
}
#endif
F_STATIC F_UINT div_sf(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM)
{
uint32_t a_sign, b_sign, r_sign;
int32_t a_exp, b_exp, r_exp;
F_UINT a_mant, b_mant, r_mant, r;
a_sign = a >> (F_SIZE - 1);
b_sign = b >> (F_SIZE - 1);
r_sign = a_sign ^ b_sign;
a_exp = (a >> MANT_SIZE) & EXP_MASK;
b_exp = (b >> MANT_SIZE) & EXP_MASK;
a_mant = a & MANT_MASK;
b_mant = b & MANT_MASK;
if (a_exp == EXP_MASK) {
if (a_mant != 0 || isnan_sf(b)) {
#if F_USE_FFLAGS
if (issignan_sf(a) || issignan_sf(b)) {
*pfflags |= FFLAG_INVALID_OP;
}
#endif
return F_QNAN;
} else if (b_exp == EXP_MASK) {
#if F_USE_FFLAGS
*pfflags |= FFLAG_INVALID_OP;
#endif
return F_QNAN;
} else {
return pack_sf(r_sign, EXP_MASK, 0);
}
} else if (b_exp == EXP_MASK) {
if (b_mant != 0) {
#if F_USE_FFLAGS
if (issignan_sf(a) || issignan_sf(b)) {
*pfflags |= FFLAG_INVALID_OP;
}
#endif
return F_QNAN;
} else {
return pack_sf(r_sign, 0, 0);
}
}
if (b_exp == 0) {
if (b_mant == 0) {
/* zero */
if (a_exp == 0 && a_mant == 0) {
#if F_USE_FFLAGS
*pfflags |= FFLAG_INVALID_OP;
#endif
return F_QNAN;
} else {
#if F_USE_FFLAGS
*pfflags |= FFLAG_DIVIDE_ZERO;
#endif
return pack_sf(r_sign, EXP_MASK, 0);
}
}
b_mant = normalize_subnormal_sf(&b_exp, b_mant);
} else {
b_mant |= (F_UINT)1 << MANT_SIZE;
}
if (a_exp == 0) {
if (a_mant == 0)
return pack_sf(r_sign, 0, 0); /* zero */
a_mant = normalize_subnormal_sf(&a_exp, a_mant);
} else {
a_mant |= (F_UINT)1 << MANT_SIZE;
}
r_exp = a_exp - b_exp + (1 << (EXP_SIZE - 1)) - 1;
r_mant = divrem_u(&r, a_mant, 0, b_mant << 2);
if (r != 0)
r_mant |= 1;
return normalize_sf(r_sign, r_exp, r_mant, rm FFLAGS_ARG);
}
#ifdef F_ULONG
/* compute sqrt(a) with a = ah*2^F_SIZE+al and a < 2^(F_SIZE - 2)
return true if not exact square. */
static int sqrtrem_u(F_UINT *pr, F_UINT ah, F_UINT al)
{
F_ULONG a, u, s;
int l, inexact;
/* 2^l >= a */
if (ah != 0) {
l = 2 * F_SIZE - clz(ah - 1);
} else {
if (al == 0) {
*pr = 0;
return 0;
}
l = F_SIZE - clz(al - 1);
}
a = ((F_ULONG)ah << F_SIZE) | al;
u = (F_ULONG)1 << ((l + 1) / 2);
for(;;) {
s = u;
u = ((a / s) + s) / 2;
if (u >= s)
break;
}
inexact = (a - s * s) != 0;
*pr = s;
return inexact;
}
#else
static int sqrtrem_u(F_UINT *pr, F_UINT a1, F_UINT a0)
{
int l, inexact;
F_UINT u, s, r, q, sq0, sq1;
/* 2^l >= a */
if (a1 != 0) {
l = 2 * F_SIZE - clz(a1 - 1);
} else {
if (a0 == 0) {
*pr = 0;
return 0;
}
l = F_SIZE - clz(a0 - 1);
}
u = (F_UINT)1 << ((l + 1) / 2);
for(;;) {
s = u;
q = divrem_u(&r, a1, a0, s);
u = (q + s) / 2;
if (u >= s)
break;
}
sq1 = mul_u(&sq0, s, s);
inexact = (sq0 != a0 || sq1 != a1);
*pr = s;
return inexact;
}
#endif
F_STATIC F_UINT sqrt_sf(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM)
{
uint32_t a_sign;
int32_t a_exp;
F_UINT a_mant;
a_sign = a >> (F_SIZE - 1);
a_exp = (a >> MANT_SIZE) & EXP_MASK;
a_mant = a & MANT_MASK;
if (a_exp == EXP_MASK) {
if (a_mant != 0) {
#if F_USE_FFLAGS
if (issignan_sf(a)) {
*pfflags |= FFLAG_INVALID_OP;
}
#endif
return F_QNAN;
} else if (a_sign) {
goto neg_error;
} else {
return a; /* +infinity */
}
}
if (a_sign) {
if (a_exp == 0 && a_mant == 0)
return a; /* -zero */
neg_error:
#if F_USE_FFLAGS
*pfflags |= FFLAG_INVALID_OP;
#endif
return F_QNAN;
}
if (a_exp == 0) {
if (a_mant == 0)
return pack_sf(0, 0, 0); /* zero */
a_mant = normalize_subnormal_sf(&a_exp, a_mant);
} else {
a_mant |= (F_UINT)1 << MANT_SIZE;
}
a_exp -= EXP_MASK / 2;
/* simpler to handle an even exponent */
if (a_exp & 1) {
a_exp--;
a_mant <<= 1;
}
a_exp = (a_exp >> 1) + EXP_MASK / 2;
a_mant <<= (F_SIZE - 4 - MANT_SIZE);
if (sqrtrem_u(&a_mant, a_mant, 0))
a_mant |= 1;
return normalize_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG);
}
/* comparisons */
F_STATIC int glue(eq_quiet_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM)
{
if (isnan_sf(a) || isnan_sf(b)) {
#if F_USE_FFLAGS
if (issignan_sf(a) || issignan_sf(b)) {
*pfflags |= FFLAG_INVALID_OP;
}
#endif
return 0;
}
if ((F_UINT)((a | b) << 1) == 0)
return 1; /* zero case */
return (a == b);
}
F_STATIC int glue(le_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM)
{
uint32_t a_sign, b_sign;
if (isnan_sf(a) || isnan_sf(b)) {
#if F_USE_FFLAGS
*pfflags |= FFLAG_INVALID_OP;
#endif
return 0;
}
a_sign = a >> (F_SIZE - 1);
b_sign = b >> (F_SIZE - 1);
if (a_sign != b_sign) {
return (a_sign || ((F_UINT)((a | b) << 1) == 0));
} else {
if (a_sign) {
return (a >= b);
} else {
return (a <= b);
}
}
}
F_STATIC int glue(lt_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM)
{
uint32_t a_sign, b_sign;
if (isnan_sf(a) || isnan_sf(b)) {
#if F_USE_FFLAGS
*pfflags |= FFLAG_INVALID_OP;
#endif
return 0;
}
a_sign = a >> (F_SIZE - 1);
b_sign = b >> (F_SIZE - 1);
if (a_sign != b_sign) {
return (a_sign && ((F_UINT)((a | b) << 1) != 0));
} else {
if (a_sign) {
return (a > b);
} else {
return (a < b);
}
}
}
/* return -1 (a < b), 0 (a = b), 1 (a > b) or 2 (a = nan or b =
nan) */
F_STATIC int glue(cmp_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM)
{
uint32_t a_sign, b_sign;
if (isnan_sf(a) || isnan_sf(b)) {
#if F_USE_FFLAGS
*pfflags |= FFLAG_INVALID_OP;
#endif
return 2;
}
a_sign = a >> (F_SIZE - 1);
b_sign = b >> (F_SIZE - 1);
if (a_sign != b_sign) {
if ((F_UINT)((a | b) << 1) != 0)
return 1 - 2 * a_sign;
else
return 0; /* -0 = +0 */
} else {
if (a < b)
return 2 * a_sign - 1;
else if (a > b)
return 1 - 2 * a_sign;
else
return 0;
}
}
/* conversions between floats */
#if F_SIZE >= 64
F_STATIC F_UINT cvt_sf32_sf(uint32_t a FFLAGS_PARAM)
{
uint32_t a_sign;
int32_t a_exp;
F_UINT a_mant;
a_mant = unpack_sf32(&a_sign, &a_exp, a);
if (a_exp == 0xff) {
if (a_mant != 0) {
/* NaN */
#if F_USE_FFLAGS
if (issignan_sf32(a)) {
*pfflags |= FFLAG_INVALID_OP;
}
#endif
return F_QNAN;
} else {
/* infinity */
return pack_sf(a_sign, EXP_MASK, 0);
}
}
if (a_exp == 0) {
if (a_mant == 0)
return pack_sf(a_sign, 0, 0); /* zero */
a_mant = normalize_subnormal_sf32(&a_exp, a_mant);
}
/* convert the exponent value */
a_exp = a_exp - 0x7f + (EXP_MASK / 2);
/* shift the mantissa */
a_mant <<= (MANT_SIZE - 23);
/* We assume the target float is large enough to that no
normalization is necessary */
return pack_sf(a_sign, a_exp, a_mant);
}
F_STATIC uint32_t glue(glue(cvt_sf, F_SIZE), _sf32)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM)
{
uint32_t a_sign;
int32_t a_exp;
F_UINT a_mant;
a_mant = unpack_sf(&a_sign, &a_exp, a);
if (a_exp == EXP_MASK) {
if (a_mant != 0) {
/* NaN */
#if F_USE_FFLAGS
if (issignan_sf(a)) {
*pfflags |= FFLAG_INVALID_OP;
}
#endif
return F_QNAN32;
} else {
/* infinity */
return pack_sf32(a_sign, 0xff, 0);
}
}
if (a_exp == 0) {
if (a_mant == 0)
return pack_sf32(a_sign, 0, 0); /* zero */
normalize_subnormal_sf(&a_exp, a_mant);
} else {
a_mant |= (F_UINT)1 << MANT_SIZE;
}
/* convert the exponent value */
a_exp = a_exp - (EXP_MASK / 2) + 0x7f;
/* shift the mantissa */
a_mant = rshift_rnd(a_mant, MANT_SIZE - (32 - 2));
return normalize_sf32(a_sign, a_exp, a_mant, rm FFLAGS_ARG);
}
#endif
#if F_SIZE >= 128
F_STATIC F_UINT cvt_sf64_sf(uint64_t a FFLAGS_PARAM)
{
uint32_t a_sign;
int32_t a_exp;
F_UINT a_mant;
a_mant = unpack_sf64(&a_sign, &a_exp, a);
if (a_exp == 0x7ff) {
if (a_mant != 0) {
/* NaN */
#if F_USE_FFLAGS
if (issignan_sf64(a)) {
*pfflags |= FFLAG_INVALID_OP;
}
#endif
return F_QNAN;
} else {
/* infinity */
return pack_sf(a_sign, EXP_MASK, 0);
}
}
if (a_exp == 0) {
if (a_mant == 0)
return pack_sf(a_sign, 0, 0); /* zero */
a_mant = normalize_subnormal_sf64(&a_exp, a_mant);
}
/* convert the exponent value */
a_exp = a_exp - 0x3ff + (EXP_MASK / 2);
/* shift the mantissa */
a_mant <<= (MANT_SIZE - 52);
return pack_sf(a_sign, a_exp, a_mant);
}
F_STATIC uint64_t glue(glue(cvt_sf, F_SIZE), _sf64)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM)
{
uint32_t a_sign;
int32_t a_exp;
F_UINT a_mant;
a_mant = unpack_sf(&a_sign, &a_exp, a);
if (a_exp == EXP_MASK) {
if (a_mant != 0) {
/* NaN */
#if F_USE_FFLAGS
if (issignan_sf(a)) {
*pfflags |= FFLAG_INVALID_OP;
}
#endif
return F_QNAN64;
} else {
/* infinity */
return pack_sf64(a_sign, 0x7ff, 0);
}
}
if (a_exp == 0) {
if (a_mant == 0)
return pack_sf64(a_sign, 0, 0); /* zero */
normalize_subnormal_sf(&a_exp, a_mant);
} else {
a_mant |= (F_UINT)1 << MANT_SIZE;
}
/* convert the exponent value */
a_exp = a_exp - (EXP_MASK / 2) + 0x3ff;
/* shift the mantissa */
a_mant = rshift_rnd(a_mant, MANT_SIZE - (64 - 2));
return normalize_sf64(a_sign, a_exp, a_mant, rm, pfflags);
}
#endif
#undef clz
#define ICVT_SIZE 32
#include "softfp_template_icvt.h"
#define ICVT_SIZE 64
#include "softfp_template_icvt.h"
/* additional libm functions */
/* return a mod b (exact) */
F_STATIC F_UINT glue(fmod_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM)
{
uint32_t a_sign;
int32_t a_exp, b_exp, n;
F_UINT a_mant, b_mant, a_abs, b_abs;
a_abs = a & ~SIGN_MASK;
b_abs = b & ~SIGN_MASK;
if (b_abs == 0 ||
a_abs >= ((F_UINT)EXP_MASK << MANT_SIZE) ||
b_abs > ((F_UINT)EXP_MASK << MANT_SIZE)) {
/* XXX: flags */
return F_QNAN;
}
if (a_abs < b_abs) {
return a; /* |a| < |b| return a */
} else if (a_abs == b_abs) {
return a & SIGN_MASK; /* |a| = |b| return copy_sign(0, a) */
}
a_sign = a >> (F_SIZE - 1);
a_exp = (a >> MANT_SIZE) & EXP_MASK;
b_exp = (b >> MANT_SIZE) & EXP_MASK;
a_mant = (a & MANT_MASK);
b_mant = (b & MANT_MASK);
if (a_exp == 0) {
a_mant = normalize_subnormal_sf(&a_exp, a_mant);
} else {
a_mant |= (F_UINT)1 << MANT_SIZE;
}
if (b_exp == 0) {
b_mant = normalize_subnormal_sf(&b_exp, b_mant);
} else {
b_mant |= (F_UINT)1 << MANT_SIZE;
}
n = a_exp - b_exp;
if (a_mant >= b_mant)
a_mant -= b_mant;
/* here a_mant < b_mant and n >= 0 */
/* multiply a_mant by 2^n */
/* XXX: do it faster */
while (n != 0) {
a_mant <<= 1;
if (a_mant >= b_mant)
a_mant -= b_mant;
n--;
}
/* Note: the rounding mode does not matter because the result is
exact */
return normalize_sf(a_sign, b_exp, a_mant << RND_SIZE, RM_RNE FFLAGS_ARG);
}
#endif /* F_NORMALIZE_ONLY */
#undef F_SIZE
#undef F_UINT
#undef F_ULONG
#undef F_UHALF
#undef MANT_SIZE
#undef EXP_SIZE
#undef EXP_MASK
#undef MANT_MASK
#undef SIGN_MASK
#undef IMANT_SIZE
#undef RND_SIZE
#undef QNAN_MASK
#undef F_QNAN
#undef F_NORMALIZE_ONLY
#undef EXP_BIAS
#undef pack_sf
#undef unpack_sf
#undef rshift_rnd
#undef round_pack_sf
#undef normalize_sf
#undef normalize2_sf
#undef issignan_sf
#undef isnan_sf
#undef add_sf
#undef mul_sf
#undef fma_sf
#undef div_sf
#undef sqrt_sf
#undef normalize_subnormal_sf
#undef divrem_u
#undef sqrtrem_u
#undef mul_u
#undef cvt_sf32_sf
#undef cvt_sf64_sf

View File

@ -0,0 +1,172 @@
/*
* SoftFP Library
*
* Copyright (c) 2016 Fabrice Bellard
*
* 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.
*/
#if ICVT_SIZE == 32
#define ICVT_UINT uint32_t
#define ICVT_INT int32_t
#elif ICVT_SIZE == 64
#define ICVT_UINT uint64_t
#define ICVT_INT int64_t
#elif ICVT_SIZE == 128
#define ICVT_UINT uint128_t
#define ICVT_INT int128_t
#else
#error unsupported icvt
#endif
/* conversions between float and integers */
static ICVT_INT glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm,
BOOL is_unsigned FFLAGS_PARAM)
{
uint32_t a_sign, addend, rnd_bits;
int32_t a_exp;
F_UINT a_mant;
ICVT_UINT r, r_max;
a_sign = a >> (F_SIZE - 1);
a_exp = (a >> MANT_SIZE) & EXP_MASK;
a_mant = a & MANT_MASK;
if (a_exp == EXP_MASK && a_mant != 0)
a_sign = 0; /* NaN is like +infinity */
if (a_exp == 0) {
a_exp = 1;
} else {
a_mant |= (F_UINT)1 << MANT_SIZE;
}
a_mant <<= RND_SIZE;
a_exp = a_exp - (EXP_MASK / 2) - MANT_SIZE;
if (is_unsigned)
r_max = (ICVT_UINT)a_sign - 1;
else
r_max = ((ICVT_UINT)1 << (ICVT_SIZE - 1)) - (ICVT_UINT)(a_sign ^ 1);
if (a_exp >= 0) {
if (a_exp <= (ICVT_SIZE - 1 - MANT_SIZE)) {
r = (ICVT_UINT)(a_mant >> RND_SIZE) << a_exp;
if (r > r_max)
goto overflow;
} else {
overflow:
#if F_USE_FFLAGS
*pfflags |= FFLAG_INVALID_OP;
#endif
return r_max;
}
} else {
a_mant = rshift_rnd(a_mant, -a_exp);
switch(rm) {
case RM_RNE:
case RM_RMM:
addend = (1 << (RND_SIZE - 1));
break;
case RM_RTZ:
addend = 0;
break;
default:
case RM_RDN:
case RM_RUP:
if (a_sign ^ (rm & 1))
addend = (1 << RND_SIZE) - 1;
else
addend = 0;
break;
}
rnd_bits = a_mant & ((1 << RND_SIZE ) - 1);
a_mant = (a_mant + addend) >> RND_SIZE;
/* half way: select even result */
if (rm == RM_RNE && rnd_bits == (1 << (RND_SIZE - 1)))
a_mant &= ~1;
if (a_mant > r_max)
goto overflow;
r = a_mant;
#if F_USE_FFLAGS
if (rnd_bits != 0)
*pfflags |= FFLAG_INEXACT;
#endif
}
if (a_sign)
r = -r;
return r;
}
F_STATIC ICVT_INT __maybe_unused glue(glue(glue(cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM)
{
return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(a, rm,
FALSE FFLAGS_ARG);
}
F_STATIC ICVT_UINT __maybe_unused glue(glue(glue(cvt_sf, F_SIZE), _u), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM)
{
return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE) (a, rm,
TRUE FFLAGS_ARG);
}
/* conversions between float and integers */
static F_UINT glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a,
RoundingModeEnum rm,
BOOL is_unsigned FFLAGS_PARAM)
{
uint32_t a_sign;
int32_t a_exp;
F_UINT a_mant;
ICVT_UINT r, mask;
int l;
if (!is_unsigned && a < 0) {
a_sign = 1;
r = -(ICVT_UINT)a;
} else {
a_sign = 0;
r = a;
}
a_exp = (EXP_MASK / 2) + F_SIZE - 2;
/* need to reduce range before generic float normalization */
l = ICVT_SIZE - glue(clz, ICVT_SIZE)(r) - (F_SIZE - 1);
if (l > 0) {
mask = r & (((ICVT_UINT)1 << l) - 1);
r = (r >> l) | ((r & mask) != 0);
a_exp += l;
}
a_mant = r;
return normalize_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG);
}
F_STATIC F_UINT __maybe_unused glue(glue(glue(cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a,
RoundingModeEnum rm
FFLAGS_PARAM)
{
return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, FALSE FFLAGS_ARG);
}
F_STATIC F_UINT __maybe_unused glue(glue(glue(cvt_u, ICVT_SIZE), _sf), F_SIZE)(ICVT_UINT a,
RoundingModeEnum rm
FFLAGS_PARAM)
{
return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, TRUE FFLAGS_ARG);
}
#undef ICVT_SIZE
#undef ICVT_INT
#undef ICVT_UINT

View File

@ -30,6 +30,8 @@ build_flags =
-std=c++2a -std=c++2a
# Enable UTF-8 long file names in SdFat # Enable UTF-8 long file names in SdFat
-DUSE_UTF8_LONG_NAMES=1 -DUSE_UTF8_LONG_NAMES=1
# Ignore error for mquickjs
-Wno-narrowing
; Board configuration ; Board configuration
board_build.flash_mode = dio board_build.flash_mode = dio

214
scripts/debugging_monitor.py Executable file
View File

@ -0,0 +1,214 @@
import sys
import argparse
import re
import threading
from datetime import datetime
from collections import deque
import time
# Try to import potentially missing packages
try:
import serial
from colorama import init, Fore, Style
import matplotlib.pyplot as plt
import matplotlib.animation as animation
except ImportError as e:
missing_package = e.name
print("\n" + "!" * 50)
print(f" Error: The required package '{missing_package}' is not installed.")
print("!" * 50)
print(f"\nTo fix this, please run the following command in your terminal:\n")
install_cmd = "pip install "
packages = []
if 'serial' in str(e): packages.append("pyserial")
if 'colorama' in str(e): packages.append("colorama")
if 'matplotlib' in str(e): packages.append("matplotlib")
print(f" {install_cmd}{' '.join(packages)}")
print("\nExiting...")
sys.exit(1)
# --- Global Variables for Data Sharing ---
# Store last 50 data points
MAX_POINTS = 50
time_data = deque(maxlen=MAX_POINTS)
free_mem_data = deque(maxlen=MAX_POINTS)
total_mem_data = deque(maxlen=MAX_POINTS)
data_lock = threading.Lock() # Prevent reading while writing
# Initialize colors
init(autoreset=True)
def get_color_for_line(line):
"""
Classify log lines by type and assign appropriate colors.
"""
line_upper = line.upper()
if any(keyword in line_upper for keyword in ["ERROR", "[ERR]", "[SCT]", "FAILED", "WARNING"]):
return Fore.RED
if "[MEM]" in line_upper or "FREE:" in line_upper:
return Fore.CYAN
if any(keyword in line_upper for keyword in ["[GFX]", "[ERS]", "DISPLAY", "RAM WRITE", "RAM COMPLETE", "REFRESH", "POWERING ON", "FRAME BUFFER", "LUT"]):
return Fore.MAGENTA
if any(keyword in line_upper for keyword in ["[EBP]", "[BMC]", "[ZIP]", "[PARSER]", "[EHP]", "LOADING EPUB", "CACHE", "DECOMPRESSED", "PARSING"]):
return Fore.GREEN
if "[ACT]" in line_upper or "ENTERING ACTIVITY" in line_upper or "EXITING ACTIVITY" in line_upper:
return Fore.YELLOW
if any(keyword in line_upper for keyword in ["RENDERED PAGE", "[LOOP]", "DURATION", "WAIT COMPLETE"]):
return Fore.BLUE
if any(keyword in line_upper for keyword in ["[CPS]", "SETTINGS", "[CLEAR_CACHE]"]):
return Fore.LIGHTYELLOW_EX
if any(keyword in line_upper for keyword in ["ESP-ROM", "BUILD:", "RST:", "BOOT:", "SPIWP:", "MODE:", "LOAD:", "ENTRY", "[SD]", "STARTING CROSSPOINT", "VERSION"]):
return Fore.LIGHTBLACK_EX
if "[RBS]" in line_upper:
return Fore.LIGHTCYAN_EX
if "[KRS]" in line_upper:
return Fore.LIGHTMAGENTA_EX
if any(keyword in line_upper for keyword in ["EINKDISPLAY:", "STATIC FRAME", "INITIALIZING", "SPI INITIALIZED", "GPIO PINS", "RESETTING", "SSD1677", "E-INK"]):
return Fore.LIGHTMAGENTA_EX
if any(keyword in line_upper for keyword in ["[FNS]", "FOOTNOTE"]):
return Fore.LIGHTGREEN_EX
if any(keyword in line_upper for keyword in ["[CHAP]", "[OPDS]", "[COF]"]):
return Fore.LIGHTYELLOW_EX
return Fore.WHITE
def parse_memory_line(line):
"""
Extracts Free and Total bytes from the specific log line.
Format: [MEM] Free: 196344 bytes, Total: 226412 bytes, Min Free: 112620 bytes
"""
# Regex to find 'Free: <digits>' and 'Total: <digits>'
match = re.search(r"Free:\s*(\d+).*Total:\s*(\d+)", line)
if match:
try:
free_bytes = int(match.group(1))
total_bytes = int(match.group(2))
return free_bytes, total_bytes
except ValueError:
return None, None
return None, None
def serial_worker(port, baud):
"""
Runs in a background thread. Handles reading serial, printing to console,
and updating the data lists.
"""
print(f"{Fore.CYAN}--- Opening {port} at {baud} baud ---{Style.RESET_ALL}")
try:
ser = serial.Serial(port, baud, timeout=0.1)
ser.dtr = False
ser.rts = False
except serial.SerialException as e:
print(f"{Fore.RED}Error opening port: {e}{Style.RESET_ALL}")
return
try:
while True:
try:
raw_data = ser.readline().decode('utf-8', errors='replace')
if not raw_data:
continue
clean_line = raw_data.strip()
if not clean_line:
continue
# Add PC timestamp
pc_time = datetime.now().strftime("%H:%M:%S")
formatted_line = re.sub(r"^\[\d+\]", f"[{pc_time}]", clean_line)
# Check for Memory Line
if "[MEM]" in formatted_line:
free_val, total_val = parse_memory_line(formatted_line)
if free_val is not None:
with data_lock:
time_data.append(pc_time)
free_mem_data.append(free_val / 1024) # Convert to KB
total_mem_data.append(total_val / 1024) # Convert to KB
# Print to console
line_color = get_color_for_line(formatted_line)
print(f"{line_color}{formatted_line}")
except OSError:
print(f"{Fore.RED}Device disconnected.{Style.RESET_ALL}")
break
except Exception as e:
# If thread is killed violently (e.g. main exit), silence errors
pass
finally:
if 'ser' in locals() and ser.is_open:
ser.close()
def update_graph(frame):
"""
Called by Matplotlib animation to redraw the chart.
"""
with data_lock:
if not time_data:
return
# Convert deques to lists for plotting
x = list(time_data)
y_free = list(free_mem_data)
y_total = list(total_mem_data)
plt.cla() # Clear axis
# Plot Total RAM
plt.plot(x, y_total, label='Total RAM (KB)', color='red', linestyle='--')
# Plot Free RAM
plt.plot(x, y_free, label='Free RAM (KB)', color='green', marker='o')
# Fill area under Free RAM
plt.fill_between(x, y_free, color='green', alpha=0.1)
plt.title("ESP32 Memory Monitor")
plt.ylabel("Memory (KB)")
plt.xlabel("Time")
plt.legend(loc='upper left')
plt.grid(True, linestyle=':', alpha=0.6)
# Rotate date labels
plt.xticks(rotation=45, ha='right')
plt.tight_layout()
def main():
parser = argparse.ArgumentParser(description="ESP32 Monitor with Graph")
parser.add_argument("port", nargs="?", default="/dev/ttyACM0", help="Serial port")
parser.add_argument("--baud", type=int, default=115200, help="Baud rate")
args = parser.parse_args()
# 1. Start the Serial Reader in a separate thread
# Daemon=True means this thread dies when the main program closes
t = threading.Thread(target=serial_worker, args=(args.port, args.baud), daemon=True)
t.start()
# 2. Set up the Graph (Main Thread)
try:
plt.style.use('light_background')
except:
pass
fig = plt.figure(figsize=(10, 6))
# Update graph every 1000ms
ani = animation.FuncAnimation(fig, update_graph, interval=1000)
try:
print(f"{Fore.YELLOW}Starting Graph Window... (Close window to exit){Style.RESET_ALL}")
plt.show()
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}Exiting...{Style.RESET_ALL}")
plt.close('all') # Force close any lingering plot windows
if __name__ == "__main__":
main()

View File

@ -0,0 +1,251 @@
#include "AppActivity.h"
#include <GfxRenderer.h>
#include <MappedInputManager.h>
#include <SDCardManager.h>
#include "../../util/StringUtils.h"
#include "fontIds.h"
void AppActivity::taskTrampoline(void* param) {
auto* self = static_cast<AppActivity*>(param);
self->displayTaskLoop();
}
void AppActivity::taskAppTrampoline(void* param) {
auto* self = static_cast<AppActivity*>(param);
self->appTaskLoop();
}
void AppActivity::onEnter() {
Activity::onEnter();
renderingMutex = xSemaphoreCreateMutex();
selectedIdx = 0;
programs.clear();
// load available applications from /apps directory
FsFile dir = SdMan.open("/apps");
if (dir && dir.isDirectory()) {
dir.rewindDirectory();
for (FsFile file = dir.openNextFile(); file; file = dir.openNextFile()) {
char name[256];
file.getName(name, sizeof(name));
std::string filename(name);
// only accept .js files
if (StringUtils::checkFileExtension(filename, ".js")) {
programs.emplace_back(std::move(filename));
}
file.close();
}
}
dir.close();
// Trigger first update
updateRequired = true;
xTaskCreate(&AppActivity::taskTrampoline, "AppActivityTask",
4096, // Stack size
this, // Parameters
1, // Priority
&displayTaskHandle // Task handle
);
}
void AppActivity::onExit() {
Activity::onExit();
auto& runner = AppRunner::getInstance();
runner.reset();
xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
}
void AppActivity::loop() {
auto& runner = AppRunner::getInstance();
if (runner.running) {
return;
}
if (runner.exited) {
if (appTaskHandle) {
vTaskDelete(appTaskHandle);
appTaskHandle = nullptr;
}
runner.reset();
updateRequired = true;
// give back rendering control
xSemaphoreGive(renderingMutex);
return;
}
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
// delegate rendering to the app
xSemaphoreTake(renderingMutex, portMAX_DELAY);
startProgram(programs[selectedIdx]); // TODO: handle errors
return;
}
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
onGoHome();
return;
}
// Handle navigation
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
// Move selection up (with wrap-around)
selectedIdx = (selectedIdx > 0) ? (selectedIdx - 1) : (programs.size() - 1);
updateRequired = true;
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
// Move selection down (with wrap around)
selectedIdx = (selectedIdx + 1) % programs.size();
updateRequired = true;
}
}
void AppActivity::displayTaskLoop() {
while (true) {
if (updateRequired) { //&& !subActivity) {
updateRequired = false;
xSemaphoreTake(renderingMutex, portMAX_DELAY);
render();
xSemaphoreGive(renderingMutex);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void AppActivity::render() const {
renderer.clearScreen();
const auto pageWidth = renderer.getScreenWidth();
const auto pageHeight = renderer.getScreenHeight();
// Draw header
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Applications", true, EpdFontFamily::BOLD);
if (programs.empty()) {
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, "No applications found", true);
} else {
// Draw selection
renderer.fillRect(0, 60 + selectedIdx * 30 - 2, pageWidth - 1, 30);
// Draw all programs
for (int i = 0; i < programs.size(); i++) {
const int programY = 60 + i * 30; // 30 pixels between programs
// Draw program name
renderer.drawText(UI_10_FONT_ID, 20, programY, programs[i].c_str(), i != selectedIdx);
}
}
// Draw help text
const auto labels = mappedInput.mapLabels("« Back", "Select", "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
// Always use standard refresh for settings screen
renderer.displayBuffer();
}
//
// APP RUNNER
//
// singleton for now
AppActivity* instance = nullptr;
int fontIdFromString(const std::string& fontStr) {
if (fontStr == "BOOKERLY_12") return BOOKERLY_12_FONT_ID;
if (fontStr == "BOOKERLY_14") return BOOKERLY_14_FONT_ID;
if (fontStr == "BOOKERLY_16") return BOOKERLY_16_FONT_ID;
if (fontStr == "BOOKERLY_18") return BOOKERLY_18_FONT_ID;
if (fontStr == "NOTOSANS_12") return NOTOSANS_12_FONT_ID;
if (fontStr == "NOTOSANS_14") return NOTOSANS_14_FONT_ID;
if (fontStr == "NOTOSANS_16") return NOTOSANS_16_FONT_ID;
if (fontStr == "NOTOSANS_18") return NOTOSANS_18_FONT_ID;
if (fontStr == "OPENDYSLEXIC_8") return OPENDYSLEXIC_8_FONT_ID;
if (fontStr == "OPENDYSLEXIC_10") return OPENDYSLEXIC_10_FONT_ID;
if (fontStr == "OPENDYSLEXIC_12") return OPENDYSLEXIC_12_FONT_ID;
if (fontStr == "OPENDYSLEXIC_14") return OPENDYSLEXIC_14_FONT_ID;
if (fontStr == "UI_10") return UI_10_FONT_ID;
if (fontStr == "UI_12") return UI_12_FONT_ID;
if (fontStr == "SMALL") return SMALL_FONT_ID;
return -1;
}
EpdFontFamily::Style styleFromString(const std::string& styleStr) {
if (styleStr == "REGULAR") return EpdFontFamily::REGULAR;
if (styleStr == "BOLD") return EpdFontFamily::BOLD;
if (styleStr == "ITALIC") return EpdFontFamily::ITALIC;
if (styleStr == "BOLD_ITALIC") return EpdFontFamily::BOLD_ITALIC;
return EpdFontFamily::REGULAR;
}
void AppActivity::startProgram(std::string programName) {
std::string fullPath = "/apps/" + programName;
FsFile file = SdMan.open(fullPath.c_str(), O_RDONLY);
assert(file && file.isOpen());
size_t fileSize = file.size();
if (fileSize == 0 || fileSize > AppRunner::MAX_PROG_SIZE) {
// TODO: show as a dialog message
Serial.printf("[%lu] [APP] Invalid program size: %u bytes, max supported = %u\n", millis(), (unsigned)fileSize,
(unsigned)AppRunner::MAX_PROG_SIZE);
file.close();
return;
}
// prepare runner
auto& runner = AppRunner::getInstance();
runner.reset();
// load program code
runner.prog.resize(fileSize + 1);
size_t bytesRead = file.read(&runner.prog[0], fileSize);
assert(bytesRead == fileSize);
runner.prog[fileSize] = '\0';
file.close();
Serial.printf("[%lu] [APP] Starting program: %s (%u bytes)\n", millis(), programName.c_str(),
(unsigned)runner.prog.size());
// clear screen before running
renderer.clearScreen();
renderer.displayBuffer();
// start new task
runner.running = true;
xTaskCreate(&AppActivity::taskAppTrampoline, "AppRuntimeTask",
4096, // Stack size
this, // Parameters
1, // Priority
&appTaskHandle // Task handle
);
Serial.printf("[%lu] [APP] Program started\n", millis());
}
void AppActivity::appTaskLoop() {
auto& runner = AppRunner::getInstance();
assert(runner.running && "program not running");
// run program code
runner.run(&renderer, &mappedInput);
// program ended
Serial.printf("[%lu] [APP] Program ended\n", millis());
runner.running = false;
runner.exited = true;
// keep task alive until main loop cleans up
while (true) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <EpdFontFamily.h>
#include <functional>
#include <string>
#include <utility>
#include <vector>
#include "../../activities/Activity.h"
#include "AppRunner.h"
class AppActivity final : public Activity {
TaskHandle_t displayTaskHandle = nullptr;
TaskHandle_t appTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
bool updateRequired = false;
const std::function<void()> onGoHome;
static void taskTrampoline(void* param);
static void taskAppTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
[[noreturn]] void appTaskLoop();
void render() const;
void startProgram(std::string programName);
public:
explicit AppActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::function<void()>& onGoHome)
: Activity("Settings", renderer, mappedInput), onGoHome(onGoHome) {}
void onEnter() override;
void onExit() override;
void loop() override;
GfxRenderer& getRenderer() { return renderer; }
MappedInputManager& getMappedInput() { return mappedInput; }
// state
std::vector<std::string> programs;
size_t selectedIdx = 0;
};

View File

@ -0,0 +1,331 @@
#include "AppRunner.h"
#include <Arduino.h>
#include <ctime>
#include <string>
#include <vector>
#include "../../fontIds.h"
extern "C" {
#include <mquickjs.h>
static JSValue js_print(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
int i;
JSValue v;
for (i = 0; i < argc; i++) {
if (i != 0) Serial.write(' ');
v = argv[i];
if (JS_IsString(ctx, v)) {
JSCStringBuf buf;
const char* str;
size_t len;
str = JS_ToCStringLen(ctx, &len, v, &buf);
Serial.write((const uint8_t*)str, len);
} else {
JS_PrintValueF(ctx, argv[i], JS_DUMP_LONG);
}
}
Serial.println();
return JS_UNDEFINED;
}
static JSValue js_date_now(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
struct timeval tv;
gettimeofday(&tv, NULL);
return JS_NewInt64(ctx, (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000));
}
static JSValue js_performance_now(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
return JS_ThrowInternalError(ctx, "js_performance_now not implemented");
}
static JSValue js_gc(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
return JS_ThrowInternalError(ctx, "js_gc not implemented");
}
static JSValue js_load(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
return JS_ThrowInternalError(ctx, "js_load not implemented");
}
static JSValue js_setTimeout(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
return JS_ThrowInternalError(ctx, "js_setTimeout not implemented");
}
static JSValue js_clearTimeout(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
return JS_ThrowInternalError(ctx, "js_clearTimeout not implemented");
}
// Crosspoint-specific functions
AppRunner& appInstance() { return AppRunner::getInstance(); }
const char* argc_err_msg = "Expected at least %d arguments, but got %d";
#define CHECK_ARGC(minArgs) \
if (argc < minArgs) { \
return JS_ThrowTypeError(ctx, argc_err_msg, minArgs, argc); \
}
#define GET_STRING_ARG(index, varName) \
JSCStringBuf varName##Buf; \
size_t varName##Len; \
const char* varName; \
varName = JS_ToCStringLen(ctx, &varName##Len, argv[index], &varName##Buf);
static JSValue js_millis(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
return JS_NewInt64(ctx, millis());
}
static JSValue js_btnIsPressed(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(1);
GET_STRING_ARG(0, buttonStr);
if (!buttonStr) {
return JS_EXCEPTION;
}
MappedInputManager::Button button;
if (strcmp(buttonStr, "B") == 0) {
button = MappedInputManager::Button::Back;
} else if (strcmp(buttonStr, "C") == 0) {
button = MappedInputManager::Button::Confirm;
} else if (strcmp(buttonStr, "L") == 0) {
button = MappedInputManager::Button::Left;
} else if (strcmp(buttonStr, "R") == 0) {
button = MappedInputManager::Button::Right;
} else if (strcmp(buttonStr, "U") == 0) {
button = MappedInputManager::Button::Up;
} else if (strcmp(buttonStr, "D") == 0) {
button = MappedInputManager::Button::Down;
} else {
return JS_ThrowRangeError(ctx, "invalid button id '%s'", buttonStr);
}
bool isPressed = appInstance().mappedInput->isPressed(button);
return JS_NewBool(isPressed);
}
static JSValue js_getScreenWidth(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
return appInstance().renderer->getScreenWidth();
}
static JSValue js_getScreenHeight(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
return appInstance().renderer->getScreenHeight();
}
static JSValue js_clearScreen(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(1);
int color;
if (JS_ToInt32(ctx, &color, argv[0])) return JS_EXCEPTION;
if (color < 0 || color > 255) {
return JS_ThrowRangeError(ctx, "color must be between 0 and 255");
}
appInstance().renderer->clearScreen((uint8_t)color);
return JS_UNDEFINED;
}
static JSValue js_displayBuffer(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(1);
int refreshMode = HalDisplay::FAST_REFRESH;
if (JS_ToInt32(ctx, &refreshMode, argv[0])) return JS_EXCEPTION;
appInstance().renderer->displayBuffer((HalDisplay::RefreshMode)refreshMode);
return JS_UNDEFINED;
}
static JSValue js_drawLine(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(5);
int x1, y1, x2, y2, state;
if (JS_ToInt32(ctx, &x1, argv[0]) || JS_ToInt32(ctx, &y1, argv[1]) || JS_ToInt32(ctx, &x2, argv[2]) ||
JS_ToInt32(ctx, &y2, argv[3]) || JS_ToInt32(ctx, &state, argv[4])) {
return JS_EXCEPTION;
}
appInstance().renderer->drawLine(x1, y1, x2, y2, state != 0);
return JS_UNDEFINED;
}
static JSValue js_drawRect(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(5);
int x, y, width, height, state;
if (JS_ToInt32(ctx, &x, argv[0]) || JS_ToInt32(ctx, &y, argv[1]) || JS_ToInt32(ctx, &width, argv[2]) ||
JS_ToInt32(ctx, &height, argv[3]) || JS_ToInt32(ctx, &state, argv[4])) {
return JS_EXCEPTION;
}
appInstance().renderer->drawRect(x, y, width, height, state != 0);
return JS_UNDEFINED;
}
static JSValue js_fillRect(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(5);
int x, y, width, height, state;
if (JS_ToInt32(ctx, &x, argv[0]) || JS_ToInt32(ctx, &y, argv[1]) || JS_ToInt32(ctx, &width, argv[2]) ||
JS_ToInt32(ctx, &height, argv[3]) || JS_ToInt32(ctx, &state, argv[4])) {
return JS_EXCEPTION;
}
appInstance().renderer->fillRect(x, y, width, height, state != 0);
return JS_UNDEFINED;
}
static JSValue js_drawImage(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(5);
JSCStringBuf buf;
size_t len; // unused for now
const char* bitmapData = JS_ToCStringLen(ctx, &len, argv[0], &buf);
int x, y, width, height;
if (!bitmapData || JS_ToInt32(ctx, &x, argv[1]) || JS_ToInt32(ctx, &y, argv[2]) || JS_ToInt32(ctx, &width, argv[3]) ||
JS_ToInt32(ctx, &height, argv[4])) {
return JS_EXCEPTION;
}
appInstance().renderer->drawImage((const uint8_t*)bitmapData, x, y, width, height);
return JS_UNDEFINED;
}
static int fontIdFromString(const char* fontIdStr) {
if (strcmp(fontIdStr, "UI10") == 0) {
return UI_10_FONT_ID;
} else if (strcmp(fontIdStr, "UI12") == 0) {
return UI_12_FONT_ID;
} else if (strcmp(fontIdStr, "SM") == 0) {
return SMALL_FONT_ID;
}
return UI_10_FONT_ID; // default
}
static EpdFontFamily::Style textStyleFromString(const char* styleStr) {
EpdFontFamily::Style style = EpdFontFamily::REGULAR;
if (strcmp(styleStr, "B") == 0) {
style = EpdFontFamily::BOLD;
} else if (strcmp(styleStr, "I") == 0) {
style = EpdFontFamily::ITALIC;
} else if (strcmp(styleStr, "J") == 0) {
style = EpdFontFamily::BOLD_ITALIC;
}
return style;
}
static JSValue js_getTextWidth(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(3);
GET_STRING_ARG(0, fontIdStr);
GET_STRING_ARG(1, text);
GET_STRING_ARG(2, styleStr);
if (!fontIdStr || !text || !styleStr) {
return JS_EXCEPTION;
}
int fontId = fontIdFromString(fontIdStr);
EpdFontFamily::Style style = textStyleFromString(styleStr);
int width = appInstance().renderer->getTextWidth(fontId, text, style);
return JS_NewInt32(ctx, width);
}
static JSValue js_drawCenteredText(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(5);
int y, black;
GET_STRING_ARG(0, fontIdStr);
GET_STRING_ARG(2, text);
GET_STRING_ARG(4, styleStr);
if (!fontIdStr || JS_ToInt32(ctx, &y, argv[1]) || !text || JS_ToInt32(ctx, &black, argv[3]) || !styleStr) {
return JS_EXCEPTION;
}
int fontId = fontIdFromString(fontIdStr);
EpdFontFamily::Style style = textStyleFromString(styleStr);
appInstance().renderer->drawCenteredText(fontId, y, text, black != 0, style);
return JS_UNDEFINED;
}
static JSValue js_drawText(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(6);
int x, y, black;
GET_STRING_ARG(0, fontIdStr);
GET_STRING_ARG(3, text);
GET_STRING_ARG(5, styleStr);
if (!fontIdStr || JS_ToInt32(ctx, &y, argv[2]) || !text || JS_ToInt32(ctx, &black, argv[4]) || !styleStr) {
return JS_EXCEPTION;
}
int fontId = fontIdFromString(fontIdStr);
EpdFontFamily::Style style = textStyleFromString(styleStr);
appInstance().renderer->drawText(fontId, x, y, text, black != 0, style);
return JS_UNDEFINED;
}
static JSValue js_drawButtonHints(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(5);
GET_STRING_ARG(0, fontIdStr);
GET_STRING_ARG(1, btn1Text);
GET_STRING_ARG(2, btn2Text);
GET_STRING_ARG(3, btn3Text);
GET_STRING_ARG(4, btn4Text);
if (!fontIdStr || !btn1Text || !btn2Text || !btn3Text || !btn4Text) {
return JS_EXCEPTION;
}
int fontId = fontIdFromString(fontIdStr);
appInstance().renderer->drawButtonHints(fontId, btn1Text, btn2Text, btn3Text, btn4Text);
return JS_UNDEFINED;
}
static JSValue js_drawSideButtonHints(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) {
CHECK_ARGC(3);
GET_STRING_ARG(0, fontIdStr);
GET_STRING_ARG(1, topBtnText);
GET_STRING_ARG(2, bottomBtnText);
if (!fontIdStr || !topBtnText || !bottomBtnText) {
return JS_EXCEPTION;
}
int fontId = fontIdFromString(fontIdStr);
appInstance().renderer->drawSideButtonHints(fontId, topBtnText, bottomBtnText);
return JS_UNDEFINED;
}
#include <crosspoint_stdlib.h>
}
AppRunner AppRunner::instance;
static void dump_error(JSContext* jsCtx) {
JSValue obj = JS_GetException(jsCtx);
JS_PrintValueF(jsCtx, obj, JS_DUMP_LONG);
}
static void serial_log_write_func(void* opaque, const void* buf, size_t buf_len) {
Serial.printf("[%lu] [MJS] %.*s", millis(), (int)buf_len, (const char*)buf);
Serial.write((const uint8_t*)buf, buf_len);
Serial.println();
}
void AppRunner::run(GfxRenderer* renderer, MappedInputManager* mappedInput) {
this->renderer = renderer;
this->mappedInput = mappedInput;
this->mem.resize(MAX_MEM_SIZE);
this->jsCtx = JS_NewContext(mem.data(), mem.size(), &js_stdlib);
JS_SetLogFunc(jsCtx, serial_log_write_func);
JSValue val;
if (JS_IsBytecode((const uint8_t*)prog.data(), prog.size())) {
Serial.printf("[%lu] [APP] Loading bytecode...\n", millis());
if (JS_RelocateBytecode(jsCtx, (uint8_t*)prog.data(), prog.size())) {
Serial.printf("[%lu] [APP] Failed to relocate bytecode\n", millis());
}
val = JS_LoadBytecode(jsCtx, (const uint8_t*)prog.data());
} else {
Serial.printf("[%lu] [APP] Parsing program from source...\n", millis());
int parse_flags = 0;
val = JS_Parse(jsCtx, prog.data(), prog.size(), "app", parse_flags);
}
if (JS_IsException(val)) {
dump_error(jsCtx);
Serial.printf("[%lu] [APP] Got exception on parsing program\n", millis());
return;
}
val = JS_Run(jsCtx, val);
if (JS_IsException(val)) {
dump_error(jsCtx);
Serial.printf("[%lu] [APP] Program exited with exception\n", millis());
return;
}
// normal exit
}

View File

@ -0,0 +1,42 @@
#pragma once
#include <GfxRenderer.h>
#include <MappedInputManager.h>
#include <cstring>
#include <vector>
extern "C" {
#include <mquickjs.h>
}
class AppRunner {
public:
static constexpr size_t MAX_PROG_SIZE = 32 * 1024; // 32KB
static constexpr size_t MAX_MEM_SIZE = 64 * 1024; // 64KB
bool running = false;
bool exited = false;
std::vector<char> prog; // need to be alloc and set before run()
std::vector<char> mem;
JSContext* jsCtx = nullptr;
GfxRenderer* renderer = nullptr;
MappedInputManager* mappedInput = nullptr;
void reset() {
this->running = false;
this->exited = false;
this->prog.clear();
this->mem.clear();
this->jsCtx = nullptr;
}
void run(GfxRenderer* renderer, MappedInputManager* mappedInput);
// need to be singleton so that the binded JS functions can access it
static AppRunner& getInstance() { return instance; }
private:
static AppRunner instance;
};

View File

@ -4,6 +4,7 @@
#include <Epub.h> #include <Epub.h>
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include <SDCardManager.h> #include <SDCardManager.h>
#include <Utf8.h>
#include <Xtc.h> #include <Xtc.h>
#include <cstring> #include <cstring>
@ -174,6 +175,7 @@ void HomeActivity::loop() {
const int continueIdx = hasContinueReading ? idx++ : -1; const int continueIdx = hasContinueReading ? idx++ : -1;
const int myLibraryIdx = idx++; const int myLibraryIdx = idx++;
const int opdsLibraryIdx = hasOpdsUrl ? idx++ : -1; const int opdsLibraryIdx = hasOpdsUrl ? idx++ : -1;
const int appsIdx = idx++;
const int fileTransferIdx = idx++; const int fileTransferIdx = idx++;
const int settingsIdx = idx; const int settingsIdx = idx;
@ -183,6 +185,8 @@ void HomeActivity::loop() {
onMyLibraryOpen(); onMyLibraryOpen();
} else if (selectorIndex == opdsLibraryIdx) { } else if (selectorIndex == opdsLibraryIdx) {
onOpdsBrowserOpen(); onOpdsBrowserOpen();
} else if (selectorIndex == appsIdx) {
onAppsOpen();
} else if (selectorIndex == fileTransferIdx) { } else if (selectorIndex == fileTransferIdx) {
onFileTransferOpen(); onFileTransferOpen();
} else if (selectorIndex == settingsIdx) { } else if (selectorIndex == settingsIdx) {
@ -366,7 +370,7 @@ void HomeActivity::render() {
while (!lines.back().empty() && renderer.getTextWidth(UI_12_FONT_ID, lines.back().c_str()) > maxLineWidth) { while (!lines.back().empty() && renderer.getTextWidth(UI_12_FONT_ID, lines.back().c_str()) > maxLineWidth) {
// Remove "..." first, then remove one UTF-8 char, then add "..." back // Remove "..." first, then remove one UTF-8 char, then add "..." back
lines.back().resize(lines.back().size() - 3); // Remove "..." lines.back().resize(lines.back().size() - 3); // Remove "..."
StringUtils::utf8RemoveLastChar(lines.back()); utf8RemoveLastChar(lines.back());
lines.back().append("..."); lines.back().append("...");
} }
break; break;
@ -375,7 +379,7 @@ void HomeActivity::render() {
int wordWidth = renderer.getTextWidth(UI_12_FONT_ID, i.c_str()); int wordWidth = renderer.getTextWidth(UI_12_FONT_ID, i.c_str());
while (wordWidth > maxLineWidth && !i.empty()) { while (wordWidth > maxLineWidth && !i.empty()) {
// Word itself is too long, trim it (UTF-8 safe) // Word itself is too long, trim it (UTF-8 safe)
StringUtils::utf8RemoveLastChar(i); utf8RemoveLastChar(i);
// Check if we have room for ellipsis // Check if we have room for ellipsis
std::string withEllipsis = i + "..."; std::string withEllipsis = i + "...";
wordWidth = renderer.getTextWidth(UI_12_FONT_ID, withEllipsis.c_str()); wordWidth = renderer.getTextWidth(UI_12_FONT_ID, withEllipsis.c_str());
@ -428,7 +432,7 @@ void HomeActivity::render() {
if (!lastBookAuthor.empty()) { if (!lastBookAuthor.empty()) {
std::string trimmedAuthor = lastBookAuthor; std::string trimmedAuthor = lastBookAuthor;
while (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) > maxLineWidth && !trimmedAuthor.empty()) { while (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) > maxLineWidth && !trimmedAuthor.empty()) {
StringUtils::utf8RemoveLastChar(trimmedAuthor); utf8RemoveLastChar(trimmedAuthor);
} }
if (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) < if (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) <
renderer.getTextWidth(UI_10_FONT_ID, lastBookAuthor.c_str())) { renderer.getTextWidth(UI_10_FONT_ID, lastBookAuthor.c_str())) {
@ -462,14 +466,14 @@ void HomeActivity::render() {
// Trim author if too long (UTF-8 safe) // Trim author if too long (UTF-8 safe)
bool wasTrimmed = false; bool wasTrimmed = false;
while (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) > maxLineWidth && !trimmedAuthor.empty()) { while (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) > maxLineWidth && !trimmedAuthor.empty()) {
StringUtils::utf8RemoveLastChar(trimmedAuthor); utf8RemoveLastChar(trimmedAuthor);
wasTrimmed = true; wasTrimmed = true;
} }
if (wasTrimmed && !trimmedAuthor.empty()) { if (wasTrimmed && !trimmedAuthor.empty()) {
// Make room for ellipsis // Make room for ellipsis
while (renderer.getTextWidth(UI_10_FONT_ID, (trimmedAuthor + "...").c_str()) > maxLineWidth && while (renderer.getTextWidth(UI_10_FONT_ID, (trimmedAuthor + "...").c_str()) > maxLineWidth &&
!trimmedAuthor.empty()) { !trimmedAuthor.empty()) {
StringUtils::utf8RemoveLastChar(trimmedAuthor); utf8RemoveLastChar(trimmedAuthor);
} }
trimmedAuthor.append("..."); trimmedAuthor.append("...");
} }
@ -503,7 +507,7 @@ void HomeActivity::render() {
// --- Bottom menu tiles --- // --- Bottom menu tiles ---
// Build menu items dynamically // Build menu items dynamically
std::vector<const char*> menuItems = {"My Library", "File Transfer", "Settings"}; std::vector<const char*> menuItems = {"My Library", "Applications", "File Transfer", "Settings"};
if (hasOpdsUrl) { if (hasOpdsUrl) {
// Insert OPDS Browser after My Library // Insert OPDS Browser after My Library
menuItems.insert(menuItems.begin() + 1, "OPDS Browser"); menuItems.insert(menuItems.begin() + 1, "OPDS Browser");

View File

@ -26,6 +26,7 @@ class HomeActivity final : public Activity {
const std::function<void()> onSettingsOpen; const std::function<void()> onSettingsOpen;
const std::function<void()> onFileTransferOpen; const std::function<void()> onFileTransferOpen;
const std::function<void()> onOpdsBrowserOpen; const std::function<void()> onOpdsBrowserOpen;
const std::function<void()> onAppsOpen;
static void taskTrampoline(void* param); static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop(); [[noreturn]] void displayTaskLoop();
@ -39,13 +40,14 @@ class HomeActivity final : public Activity {
explicit HomeActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, explicit HomeActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& onContinueReading, const std::function<void()>& onMyLibraryOpen, const std::function<void()>& onContinueReading, const std::function<void()>& onMyLibraryOpen,
const std::function<void()>& onSettingsOpen, const std::function<void()>& onFileTransferOpen, const std::function<void()>& onSettingsOpen, const std::function<void()>& onFileTransferOpen,
const std::function<void()>& onOpdsBrowserOpen) const std::function<void()>& onOpdsBrowserOpen, const std::function<void()>& onAppsOpen)
: Activity("Home", renderer, mappedInput), : Activity("Home", renderer, mappedInput),
onContinueReading(onContinueReading), onContinueReading(onContinueReading),
onMyLibraryOpen(onMyLibraryOpen), onMyLibraryOpen(onMyLibraryOpen),
onSettingsOpen(onSettingsOpen), onSettingsOpen(onSettingsOpen),
onFileTransferOpen(onFileTransferOpen), onFileTransferOpen(onFileTransferOpen),
onOpdsBrowserOpen(onOpdsBrowserOpen) {} onOpdsBrowserOpen(onOpdsBrowserOpen),
onAppsOpen(onAppsOpen) {}
void onEnter() override; void onEnter() override;
void onExit() override; void onExit() override;
void loop() override; void loop() override;

View File

@ -570,8 +570,8 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in
availableTitleSpace = rendererableScreenWidth - titleMarginLeft - titleMarginRight; availableTitleSpace = rendererableScreenWidth - titleMarginLeft - titleMarginRight;
titleMarginLeftAdjusted = titleMarginLeft; titleMarginLeftAdjusted = titleMarginLeft;
} }
while (titleWidth > availableTitleSpace && title.length() > 11) { if (titleWidth > availableTitleSpace) {
title.replace(title.length() - 8, 8, "..."); title = renderer.truncatedText(SMALL_FONT_ID, title.c_str(), availableTitleSpace);
titleWidth = renderer.getTextWidth(SMALL_FONT_ID, title.c_str()); titleWidth = renderer.getTextWidth(SMALL_FONT_ID, title.c_str());
} }
} }

View File

@ -533,8 +533,8 @@ void TxtReaderActivity::renderStatusBar(const int orientedMarginRight, const int
std::string title = txt->getTitle(); std::string title = txt->getTitle();
int titleWidth = renderer.getTextWidth(SMALL_FONT_ID, title.c_str()); int titleWidth = renderer.getTextWidth(SMALL_FONT_ID, title.c_str());
while (titleWidth > availableTextWidth && title.length() > 11) { if (titleWidth > availableTextWidth) {
title.replace(title.length() - 8, 8, "..."); title = renderer.truncatedText(SMALL_FONT_ID, title.c_str(), availableTextWidth);
titleWidth = renderer.getTextWidth(SMALL_FONT_ID, title.c_str()); titleWidth = renderer.getTextWidth(SMALL_FONT_ID, title.c_str());
} }

View File

@ -15,6 +15,7 @@
#include "KOReaderCredentialStore.h" #include "KOReaderCredentialStore.h"
#include "MappedInputManager.h" #include "MappedInputManager.h"
#include "RecentBooksStore.h" #include "RecentBooksStore.h"
#include "activities/app/AppActivity.h"
#include "activities/boot_sleep/BootActivity.h" #include "activities/boot_sleep/BootActivity.h"
#include "activities/boot_sleep/SleepActivity.h" #include "activities/boot_sleep/SleepActivity.h"
#include "activities/browser/OpdsBookBrowserActivity.h" #include "activities/browser/OpdsBookBrowserActivity.h"
@ -236,10 +237,15 @@ void onGoToBrowser() {
enterNewActivity(new OpdsBookBrowserActivity(renderer, mappedInputManager, onGoHome)); enterNewActivity(new OpdsBookBrowserActivity(renderer, mappedInputManager, onGoHome));
} }
void onGoToApps() {
exitActivity();
enterNewActivity(new AppActivity(renderer, mappedInputManager, onGoHome));
}
void onGoHome() { void onGoHome() {
exitActivity(); exitActivity();
enterNewActivity(new HomeActivity(renderer, mappedInputManager, onContinueReading, onGoToMyLibrary, onGoToSettings, enterNewActivity(new HomeActivity(renderer, mappedInputManager, onContinueReading, onGoToMyLibrary, onGoToSettings,
onGoToFileTransfer, onGoToBrowser)); onGoToFileTransfer, onGoToBrowser, onGoToApps));
} }
void setupDisplayAndFonts() { void setupDisplayAndFonts() {

View File

@ -61,23 +61,4 @@ bool checkFileExtension(const String& fileName, const char* extension) {
return localFile.endsWith(localExtension); return localFile.endsWith(localExtension);
} }
size_t utf8RemoveLastChar(std::string& str) {
if (str.empty()) return 0;
size_t pos = str.size() - 1;
// Walk back to find the start of the last UTF-8 character
// UTF-8 continuation bytes start with 10xxxxxx (0x80-0xBF)
while (pos > 0 && (static_cast<unsigned char>(str[pos]) & 0xC0) == 0x80) {
--pos;
}
str.resize(pos);
return pos;
}
// Truncate string by removing N UTF-8 characters from the end
void utf8TruncateChars(std::string& str, const size_t numChars) {
for (size_t i = 0; i < numChars && !str.empty(); ++i) {
utf8RemoveLastChar(str);
}
}
} // namespace StringUtils } // namespace StringUtils

View File

@ -19,10 +19,4 @@ std::string sanitizeFilename(const std::string& name, size_t maxLength = 100);
bool checkFileExtension(const std::string& fileName, const char* extension); bool checkFileExtension(const std::string& fileName, const char* extension);
bool checkFileExtension(const String& fileName, const char* extension); bool checkFileExtension(const String& fileName, const char* extension);
// UTF-8 safe string truncation - removes one character from the end
// Returns the new size after removing one UTF-8 character
size_t utf8RemoveLastChar(std::string& str);
// Truncate string by removing N UTF-8 characters from the end
void utf8TruncateChars(std::string& str, size_t numChars);
} // namespace StringUtils } // namespace StringUtils