├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── bridge.c ├── bridge.h ├── cutils.c ├── cutils.h ├── examples └── main.go ├── go.mod ├── go.sum ├── libbf.c ├── libbf.h ├── libregexp-opcode.h ├── libregexp.c ├── libregexp.h ├── libunicode-table.h ├── libunicode.c ├── libunicode.h ├── list.h ├── quickjs-atom.h ├── quickjs-opcode.h ├── quickjs.c ├── quickjs.go ├── quickjs.h ├── quickjs_test.go └── version.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set 2 | * text=auto 3 | 4 | # Require Unix line endings 5 | * text eol=lf 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Kenta Iwasaki 4 | Copyright (c) 2017-2020 Fabrice Bellard 5 | Copyright (c) 2017-2020 Charlie Gordon 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quickjs 2 | 3 | [![MIT License](https://img.shields.io/apm/l/atomic-design-ui.svg?)](LICENSE) 4 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/lithdew/quickjs) 5 | [![Discord Chat](https://img.shields.io/discord/697002823123992617)](https://discord.gg/HZEbkeQ) 6 | 7 | Go bindings to [QuickJS](https://bellard.org/quickjs/): a fast, small, and embeddable [ES2020](https://tc39.github.io/ecma262/) JavaScript interpreter. 8 | 9 | These bindings are a WIP and do not match full parity with QuickJS' API, though expose just enough features to be usable. The version of QuickJS that these bindings bind to may be located [here](version.h). 10 | 11 | These bindings have been tested to cross-compile and run successfully on Linux, Windows, and Mac using gcc-7 and mingw32 without any addtional compiler or linker flags. 12 | 13 | ## Usage 14 | 15 | ``` 16 | $ go get github.com/lithdew/quickjs 17 | ``` 18 | 19 | ## Guidelines 20 | 21 | 1. Free `quickjs.Runtime` and `quickjs.Context` once you are done using them. 22 | 2. Free `quickjs.Value`'s returned by `Eval()` and `EvalFile()`. All other values do not need to be freed, as they get garbage-collected. 23 | 3. You may access the stacktrace of an error returned by `Eval()` or `EvalFile()` by casting it to a `*quickjs.Error`. 24 | 4. Make new copies of arguments should you want to return them in functions you created. 25 | 5. Make sure to call `runtime.LockOSThread()` to ensure that QuickJS always operates in the exact same thread. 26 | 27 | ## Example 28 | 29 | The full example code below may be found by clicking [here](examples/main.go). Find more API examples [here](quickjs_test.go). 30 | 31 | ```go 32 | package main 33 | 34 | import ( 35 | "errors" 36 | "flag" 37 | "fmt" 38 | "github.com/lithdew/quickjs" 39 | "strings" 40 | ) 41 | 42 | func check(err error) { 43 | if err != nil { 44 | var evalErr *quickjs.Error 45 | if errors.As(err, &evalErr) { 46 | fmt.Println(evalErr.Cause) 47 | fmt.Println(evalErr.Stack) 48 | } 49 | panic(err) 50 | } 51 | } 52 | 53 | func main() { 54 | runtime := quickjs.NewRuntime() 55 | defer runtime.Free() 56 | 57 | context := runtime.NewContext() 58 | defer context.Free() 59 | 60 | globals := context.Globals() 61 | 62 | // Test evaluating template strings. 63 | 64 | result, err := context.Eval("`Hello world! 2 ** 8 = ${2 ** 8}.`") 65 | check(err) 66 | defer result.Free() 67 | 68 | fmt.Println(result.String()) 69 | fmt.Println() 70 | 71 | // Test evaluating numeric expressions. 72 | 73 | result, err = context.Eval(`1 + 2 * 100 - 3 + Math.sin(10)`) 74 | check(err) 75 | defer result.Free() 76 | 77 | fmt.Println(result.Int64()) 78 | fmt.Println() 79 | 80 | // Test evaluating big integer expressions. 81 | 82 | result, err = context.Eval(`128n ** 16n`) 83 | check(err) 84 | defer result.Free() 85 | 86 | fmt.Println(result.BigInt()) 87 | fmt.Println() 88 | 89 | // Test evaluating big decimal expressions. 90 | 91 | result, err = context.Eval(`128l ** 12l`) 92 | check(err) 93 | defer result.Free() 94 | 95 | fmt.Println(result.BigFloat()) 96 | fmt.Println() 97 | 98 | // Test evaluating boolean expressions. 99 | 100 | result, err = context.Eval(`false && true`) 101 | check(err) 102 | defer result.Free() 103 | 104 | fmt.Println(result.Bool()) 105 | fmt.Println() 106 | 107 | // Test setting and calling functions. 108 | 109 | A := func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value { 110 | fmt.Println("A got called!") 111 | return ctx.Null() 112 | } 113 | 114 | B := func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value { 115 | fmt.Println("B got called!") 116 | return ctx.Null() 117 | } 118 | 119 | globals.Set("A", context.Function(A)) 120 | globals.Set("B", context.Function(B)) 121 | 122 | _, err = context.Eval(`for (let i = 0; i < 10; i++) { if (i % 2 === 0) A(); else B(); }`) 123 | check(err) 124 | 125 | fmt.Println() 126 | 127 | // Test setting global variables. 128 | 129 | _, err = context.Eval(`HELLO = "world"; TEST = false;`) 130 | check(err) 131 | 132 | names, err := globals.PropertyNames() 133 | check(err) 134 | 135 | fmt.Println("Globals:") 136 | for _, name := range names { 137 | val := globals.GetByAtom(name.Atom) 138 | defer val.Free() 139 | 140 | fmt.Printf("'%s': %s\n", name, val) 141 | } 142 | fmt.Println() 143 | 144 | // Test evaluating arbitrary expressions from flag arguments. 145 | 146 | flag.Parse() 147 | if flag.NArg() == 0 { 148 | return 149 | } 150 | 151 | result, err = context.Eval(strings.Join(flag.Args(), " ")) 152 | check(err) 153 | defer result.Free() 154 | 155 | if result.IsObject() { 156 | names, err := result.PropertyNames() 157 | check(err) 158 | 159 | fmt.Println("Object:") 160 | for _, name := range names { 161 | val := result.GetByAtom(name.Atom) 162 | defer val.Free() 163 | 164 | fmt.Printf("'%s': %s\n", name, val) 165 | } 166 | } else { 167 | fmt.Println(result.String()) 168 | } 169 | } 170 | ``` 171 | 172 | ``` 173 | $ go run examples/main.go '(() => ({hello: "world", test: 2 ** 3}))()' 174 | Hello world! 2 ** 8 = 256. 175 | 176 | 197 177 | 178 | 5192296858534827628530496329220096 179 | 180 | 1.9342813113834066795e+25 181 | 182 | false 183 | 184 | A got called! 185 | B got called! 186 | A got called! 187 | B got called! 188 | A got called! 189 | B got called! 190 | A got called! 191 | B got called! 192 | A got called! 193 | B got called! 194 | 195 | Globals: 196 | 'Object': function Object() { 197 | [native code] 198 | } 199 | 'Function': function Function() { 200 | [native code] 201 | } 202 | 'Error': function Error() { 203 | [native code] 204 | } 205 | 'EvalError': function EvalError() { 206 | [native code] 207 | } 208 | 'RangeError': function RangeError() { 209 | [native code] 210 | } 211 | 'ReferenceError': function ReferenceError() { 212 | [native code] 213 | } 214 | 'SyntaxError': function SyntaxError() { 215 | [native code] 216 | } 217 | 'TypeError': function TypeError() { 218 | [native code] 219 | } 220 | 'URIError': function URIError() { 221 | [native code] 222 | } 223 | 'InternalError': function InternalError() { 224 | [native code] 225 | } 226 | 'AggregateError': function AggregateError() { 227 | [native code] 228 | } 229 | 'Array': function Array() { 230 | [native code] 231 | } 232 | 'parseInt': function parseInt() { 233 | [native code] 234 | } 235 | 'parseFloat': function parseFloat() { 236 | [native code] 237 | } 238 | 'isNaN': function isNaN() { 239 | [native code] 240 | } 241 | 'isFinite': function isFinite() { 242 | [native code] 243 | } 244 | 'decodeURI': function decodeURI() { 245 | [native code] 246 | } 247 | 'decodeURIComponent': function decodeURIComponent() { 248 | [native code] 249 | } 250 | 'encodeURI': function encodeURI() { 251 | [native code] 252 | } 253 | 'encodeURIComponent': function encodeURIComponent() { 254 | [native code] 255 | } 256 | 'escape': function escape() { 257 | [native code] 258 | } 259 | 'unescape': function unescape() { 260 | [native code] 261 | } 262 | 'Infinity': Infinity 263 | 'NaN': NaN 264 | 'undefined': undefined 265 | '__date_clock': function __date_clock() { 266 | [native code] 267 | } 268 | 'Number': function Number() { 269 | [native code] 270 | } 271 | 'Boolean': function Boolean() { 272 | [native code] 273 | } 274 | 'String': function String() { 275 | [native code] 276 | } 277 | 'Math': [object Math] 278 | 'Reflect': [object Object] 279 | 'Symbol': function Symbol() { 280 | [native code] 281 | } 282 | 'eval': function eval() { 283 | [native code] 284 | } 285 | 'globalThis': [object Object] 286 | 'Date': function Date() { 287 | [native code] 288 | } 289 | 'RegExp': function RegExp() { 290 | [native code] 291 | } 292 | 'JSON': [object JSON] 293 | 'Proxy': function Proxy() { 294 | [native code] 295 | } 296 | 'Map': function Map() { 297 | [native code] 298 | } 299 | 'Set': function Set() { 300 | [native code] 301 | } 302 | 'WeakMap': function WeakMap() { 303 | [native code] 304 | } 305 | 'WeakSet': function WeakSet() { 306 | [native code] 307 | } 308 | 'ArrayBuffer': function ArrayBuffer() { 309 | [native code] 310 | } 311 | 'SharedArrayBuffer': function SharedArrayBuffer() { 312 | [native code] 313 | } 314 | 'Uint8ClampedArray': function Uint8ClampedArray() { 315 | [native code] 316 | } 317 | 'Int8Array': function Int8Array() { 318 | [native code] 319 | } 320 | 'Uint8Array': function Uint8Array() { 321 | [native code] 322 | } 323 | 'Int16Array': function Int16Array() { 324 | [native code] 325 | } 326 | 'Uint16Array': function Uint16Array() { 327 | [native code] 328 | } 329 | 'Int32Array': function Int32Array() { 330 | [native code] 331 | } 332 | 'Uint32Array': function Uint32Array() { 333 | [native code] 334 | } 335 | 'BigInt64Array': function BigInt64Array() { 336 | [native code] 337 | } 338 | 'BigUint64Array': function BigUint64Array() { 339 | [native code] 340 | } 341 | 'Float32Array': function Float32Array() { 342 | [native code] 343 | } 344 | 'Float64Array': function Float64Array() { 345 | [native code] 346 | } 347 | 'DataView': function DataView() { 348 | [native code] 349 | } 350 | 'Atomics': [object Atomics] 351 | 'Promise': function Promise() { 352 | [native code] 353 | } 354 | 'BigInt': function BigInt() { 355 | [native code] 356 | } 357 | 'BigFloat': function BigFloat() { 358 | [native code] 359 | } 360 | 'BigFloatEnv': function BigFloatEnv() { 361 | [native code] 362 | } 363 | 'BigDecimal': function BigDecimal() { 364 | [native code] 365 | } 366 | 'Operators': function Operators() { 367 | [native code] 368 | } 369 | 'A': function() { return proxy.call(this, id, ...arguments); } 370 | 'B': function() { return proxy.call(this, id, ...arguments); } 371 | 'HELLO': world 372 | 'TEST': false 373 | 374 | Object: 375 | 'hello': world 376 | 'test': 8 377 | ``` 378 | 379 | ## License 380 | 381 | QuickJS is released under the MIT license. 382 | 383 | QuickJS bindings are copyright Kenta Iwasaki, with code copyright Fabrice Bellard and Charlie Gordon. 384 | 385 | -------------------------------------------------------------------------------- /bridge.c: -------------------------------------------------------------------------------- 1 | #include "_cgo_export.h" 2 | 3 | JSValue InvokeProxy(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { 4 | return proxy(ctx, this_val, argc, argv); 5 | } -------------------------------------------------------------------------------- /bridge.h: -------------------------------------------------------------------------------- 1 | #include "stdlib.h" 2 | #include "quickjs.h" 3 | 4 | extern JSValue InvokeProxy(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); 5 | 6 | static JSValue JS_NewNull() { return JS_NULL; } 7 | static JSValue JS_NewUndefined() { return JS_UNDEFINED; } 8 | static JSValue JS_NewUninitialized() { return JS_UNINITIALIZED; } 9 | 10 | static JSValue ThrowSyntaxError(JSContext *ctx, const char *fmt) { return JS_ThrowSyntaxError(ctx, "%s", fmt); } 11 | static JSValue ThrowTypeError(JSContext *ctx, const char *fmt) { return JS_ThrowTypeError(ctx, "%s", fmt); } 12 | static JSValue ThrowReferenceError(JSContext *ctx, const char *fmt) { return JS_ThrowReferenceError(ctx, "%s", fmt); } 13 | static JSValue ThrowRangeError(JSContext *ctx, const char *fmt) { return JS_ThrowRangeError(ctx, "%s", fmt); } 14 | static JSValue ThrowInternalError(JSContext *ctx, const char *fmt) { return JS_ThrowInternalError(ctx, "%s", fmt); } -------------------------------------------------------------------------------- /cutils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * C utilities 3 | * 4 | * Copyright (c) 2017 Fabrice Bellard 5 | * Copyright (c) 2018 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "cutils.h" 31 | 32 | void pstrcpy(char *buf, int buf_size, const char *str) 33 | { 34 | int c; 35 | char *q = buf; 36 | 37 | if (buf_size <= 0) 38 | return; 39 | 40 | for(;;) { 41 | c = *str++; 42 | if (c == 0 || q >= buf + buf_size - 1) 43 | break; 44 | *q++ = c; 45 | } 46 | *q = '\0'; 47 | } 48 | 49 | /* strcat and truncate. */ 50 | char *pstrcat(char *buf, int buf_size, const char *s) 51 | { 52 | int len; 53 | len = strlen(buf); 54 | if (len < buf_size) 55 | pstrcpy(buf + len, buf_size - len, s); 56 | return buf; 57 | } 58 | 59 | int strstart(const char *str, const char *val, const char **ptr) 60 | { 61 | const char *p, *q; 62 | p = str; 63 | q = val; 64 | while (*q != '\0') { 65 | if (*p != *q) 66 | return 0; 67 | p++; 68 | q++; 69 | } 70 | if (ptr) 71 | *ptr = p; 72 | return 1; 73 | } 74 | 75 | int has_suffix(const char *str, const char *suffix) 76 | { 77 | size_t len = strlen(str); 78 | size_t slen = strlen(suffix); 79 | return (len >= slen && !memcmp(str + len - slen, suffix, slen)); 80 | } 81 | 82 | /* Dynamic buffer package */ 83 | 84 | static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size) 85 | { 86 | return realloc(ptr, size); 87 | } 88 | 89 | void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func) 90 | { 91 | memset(s, 0, sizeof(*s)); 92 | if (!realloc_func) 93 | realloc_func = dbuf_default_realloc; 94 | s->opaque = opaque; 95 | s->realloc_func = realloc_func; 96 | } 97 | 98 | void dbuf_init(DynBuf *s) 99 | { 100 | dbuf_init2(s, NULL, NULL); 101 | } 102 | 103 | /* return < 0 if error */ 104 | int dbuf_realloc(DynBuf *s, size_t new_size) 105 | { 106 | size_t size; 107 | uint8_t *new_buf; 108 | if (new_size > s->allocated_size) { 109 | if (s->error) 110 | return -1; 111 | size = s->allocated_size * 3 / 2; 112 | if (size > new_size) 113 | new_size = size; 114 | new_buf = s->realloc_func(s->opaque, s->buf, new_size); 115 | if (!new_buf) { 116 | s->error = TRUE; 117 | return -1; 118 | } 119 | s->buf = new_buf; 120 | s->allocated_size = new_size; 121 | } 122 | return 0; 123 | } 124 | 125 | int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len) 126 | { 127 | size_t end; 128 | end = offset + len; 129 | if (dbuf_realloc(s, end)) 130 | return -1; 131 | memcpy(s->buf + offset, data, len); 132 | if (end > s->size) 133 | s->size = end; 134 | return 0; 135 | } 136 | 137 | int dbuf_put(DynBuf *s, const uint8_t *data, size_t len) 138 | { 139 | if (unlikely((s->size + len) > s->allocated_size)) { 140 | if (dbuf_realloc(s, s->size + len)) 141 | return -1; 142 | } 143 | memcpy(s->buf + s->size, data, len); 144 | s->size += len; 145 | return 0; 146 | } 147 | 148 | int dbuf_put_self(DynBuf *s, size_t offset, size_t len) 149 | { 150 | if (unlikely((s->size + len) > s->allocated_size)) { 151 | if (dbuf_realloc(s, s->size + len)) 152 | return -1; 153 | } 154 | memcpy(s->buf + s->size, s->buf + offset, len); 155 | s->size += len; 156 | return 0; 157 | } 158 | 159 | int dbuf_putc(DynBuf *s, uint8_t c) 160 | { 161 | return dbuf_put(s, &c, 1); 162 | } 163 | 164 | int dbuf_putstr(DynBuf *s, const char *str) 165 | { 166 | return dbuf_put(s, (const uint8_t *)str, strlen(str)); 167 | } 168 | 169 | int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, 170 | const char *fmt, ...) 171 | { 172 | va_list ap; 173 | char buf[128]; 174 | int len; 175 | 176 | va_start(ap, fmt); 177 | len = vsnprintf(buf, sizeof(buf), fmt, ap); 178 | va_end(ap); 179 | if (len < sizeof(buf)) { 180 | /* fast case */ 181 | return dbuf_put(s, (uint8_t *)buf, len); 182 | } else { 183 | if (dbuf_realloc(s, s->size + len + 1)) 184 | return -1; 185 | va_start(ap, fmt); 186 | vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size, 187 | fmt, ap); 188 | va_end(ap); 189 | s->size += len; 190 | } 191 | return 0; 192 | } 193 | 194 | void dbuf_free(DynBuf *s) 195 | { 196 | /* we test s->buf as a fail safe to avoid crashing if dbuf_free() 197 | is called twice */ 198 | if (s->buf) { 199 | s->realloc_func(s->opaque, s->buf, 0); 200 | } 201 | memset(s, 0, sizeof(*s)); 202 | } 203 | 204 | /* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes 205 | are output. */ 206 | int unicode_to_utf8(uint8_t *buf, unsigned int c) 207 | { 208 | uint8_t *q = buf; 209 | 210 | if (c < 0x80) { 211 | *q++ = c; 212 | } else { 213 | if (c < 0x800) { 214 | *q++ = (c >> 6) | 0xc0; 215 | } else { 216 | if (c < 0x10000) { 217 | *q++ = (c >> 12) | 0xe0; 218 | } else { 219 | if (c < 0x00200000) { 220 | *q++ = (c >> 18) | 0xf0; 221 | } else { 222 | if (c < 0x04000000) { 223 | *q++ = (c >> 24) | 0xf8; 224 | } else if (c < 0x80000000) { 225 | *q++ = (c >> 30) | 0xfc; 226 | *q++ = ((c >> 24) & 0x3f) | 0x80; 227 | } else { 228 | return 0; 229 | } 230 | *q++ = ((c >> 18) & 0x3f) | 0x80; 231 | } 232 | *q++ = ((c >> 12) & 0x3f) | 0x80; 233 | } 234 | *q++ = ((c >> 6) & 0x3f) | 0x80; 235 | } 236 | *q++ = (c & 0x3f) | 0x80; 237 | } 238 | return q - buf; 239 | } 240 | 241 | static const unsigned int utf8_min_code[5] = { 242 | 0x80, 0x800, 0x10000, 0x00200000, 0x04000000, 243 | }; 244 | 245 | static const unsigned char utf8_first_code_mask[5] = { 246 | 0x1f, 0xf, 0x7, 0x3, 0x1, 247 | }; 248 | 249 | /* return -1 if error. *pp is not updated in this case. max_len must 250 | be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */ 251 | int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp) 252 | { 253 | int l, c, b, i; 254 | 255 | c = *p++; 256 | if (c < 0x80) { 257 | *pp = p; 258 | return c; 259 | } 260 | switch(c) { 261 | case 0xc0 ... 0xdf: 262 | l = 1; 263 | break; 264 | case 0xe0 ... 0xef: 265 | l = 2; 266 | break; 267 | case 0xf0 ... 0xf7: 268 | l = 3; 269 | break; 270 | case 0xf8 ... 0xfb: 271 | l = 4; 272 | break; 273 | case 0xfc ... 0xfd: 274 | l = 5; 275 | break; 276 | default: 277 | return -1; 278 | } 279 | /* check that we have enough characters */ 280 | if (l > (max_len - 1)) 281 | return -1; 282 | c &= utf8_first_code_mask[l - 1]; 283 | for(i = 0; i < l; i++) { 284 | b = *p++; 285 | if (b < 0x80 || b >= 0xc0) 286 | return -1; 287 | c = (c << 6) | (b & 0x3f); 288 | } 289 | if (c < utf8_min_code[l - 1]) 290 | return -1; 291 | *pp = p; 292 | return c; 293 | } 294 | 295 | #if 0 296 | 297 | #if defined(EMSCRIPTEN) || defined(__ANDROID__) 298 | 299 | static void *rqsort_arg; 300 | static int (*rqsort_cmp)(const void *, const void *, void *); 301 | 302 | static int rqsort_cmp2(const void *p1, const void *p2) 303 | { 304 | return rqsort_cmp(p1, p2, rqsort_arg); 305 | } 306 | 307 | /* not reentrant, but not needed with emscripten */ 308 | void rqsort(void *base, size_t nmemb, size_t size, 309 | int (*cmp)(const void *, const void *, void *), 310 | void *arg) 311 | { 312 | rqsort_arg = arg; 313 | rqsort_cmp = cmp; 314 | qsort(base, nmemb, size, rqsort_cmp2); 315 | } 316 | 317 | #endif 318 | 319 | #else 320 | 321 | typedef void (*exchange_f)(void *a, void *b, size_t size); 322 | typedef int (*cmp_f)(const void *, const void *, void *opaque); 323 | 324 | static void exchange_bytes(void *a, void *b, size_t size) { 325 | uint8_t *ap = (uint8_t *)a; 326 | uint8_t *bp = (uint8_t *)b; 327 | 328 | while (size-- != 0) { 329 | uint8_t t = *ap; 330 | *ap++ = *bp; 331 | *bp++ = t; 332 | } 333 | } 334 | 335 | static void exchange_one_byte(void *a, void *b, size_t size) { 336 | uint8_t *ap = (uint8_t *)a; 337 | uint8_t *bp = (uint8_t *)b; 338 | uint8_t t = *ap; 339 | *ap = *bp; 340 | *bp = t; 341 | } 342 | 343 | static void exchange_int16s(void *a, void *b, size_t size) { 344 | uint16_t *ap = (uint16_t *)a; 345 | uint16_t *bp = (uint16_t *)b; 346 | 347 | for (size /= sizeof(uint16_t); size-- != 0;) { 348 | uint16_t t = *ap; 349 | *ap++ = *bp; 350 | *bp++ = t; 351 | } 352 | } 353 | 354 | static void exchange_one_int16(void *a, void *b, size_t size) { 355 | uint16_t *ap = (uint16_t *)a; 356 | uint16_t *bp = (uint16_t *)b; 357 | uint16_t t = *ap; 358 | *ap = *bp; 359 | *bp = t; 360 | } 361 | 362 | static void exchange_int32s(void *a, void *b, size_t size) { 363 | uint32_t *ap = (uint32_t *)a; 364 | uint32_t *bp = (uint32_t *)b; 365 | 366 | for (size /= sizeof(uint32_t); size-- != 0;) { 367 | uint32_t t = *ap; 368 | *ap++ = *bp; 369 | *bp++ = t; 370 | } 371 | } 372 | 373 | static void exchange_one_int32(void *a, void *b, size_t size) { 374 | uint32_t *ap = (uint32_t *)a; 375 | uint32_t *bp = (uint32_t *)b; 376 | uint32_t t = *ap; 377 | *ap = *bp; 378 | *bp = t; 379 | } 380 | 381 | static void exchange_int64s(void *a, void *b, size_t size) { 382 | uint64_t *ap = (uint64_t *)a; 383 | uint64_t *bp = (uint64_t *)b; 384 | 385 | for (size /= sizeof(uint64_t); size-- != 0;) { 386 | uint64_t t = *ap; 387 | *ap++ = *bp; 388 | *bp++ = t; 389 | } 390 | } 391 | 392 | static void exchange_one_int64(void *a, void *b, size_t size) { 393 | uint64_t *ap = (uint64_t *)a; 394 | uint64_t *bp = (uint64_t *)b; 395 | uint64_t t = *ap; 396 | *ap = *bp; 397 | *bp = t; 398 | } 399 | 400 | static void exchange_int128s(void *a, void *b, size_t size) { 401 | uint64_t *ap = (uint64_t *)a; 402 | uint64_t *bp = (uint64_t *)b; 403 | 404 | for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) { 405 | uint64_t t = ap[0]; 406 | uint64_t u = ap[1]; 407 | ap[0] = bp[0]; 408 | ap[1] = bp[1]; 409 | bp[0] = t; 410 | bp[1] = u; 411 | } 412 | } 413 | 414 | static void exchange_one_int128(void *a, void *b, size_t size) { 415 | uint64_t *ap = (uint64_t *)a; 416 | uint64_t *bp = (uint64_t *)b; 417 | uint64_t t = ap[0]; 418 | uint64_t u = ap[1]; 419 | ap[0] = bp[0]; 420 | ap[1] = bp[1]; 421 | bp[0] = t; 422 | bp[1] = u; 423 | } 424 | 425 | static inline exchange_f exchange_func(const void *base, size_t size) { 426 | switch (((uintptr_t)base | (uintptr_t)size) & 15) { 427 | case 0: 428 | if (size == sizeof(uint64_t) * 2) 429 | return exchange_one_int128; 430 | else 431 | return exchange_int128s; 432 | case 8: 433 | if (size == sizeof(uint64_t)) 434 | return exchange_one_int64; 435 | else 436 | return exchange_int64s; 437 | case 4: 438 | case 12: 439 | if (size == sizeof(uint32_t)) 440 | return exchange_one_int32; 441 | else 442 | return exchange_int32s; 443 | case 2: 444 | case 6: 445 | case 10: 446 | case 14: 447 | if (size == sizeof(uint16_t)) 448 | return exchange_one_int16; 449 | else 450 | return exchange_int16s; 451 | default: 452 | if (size == 1) 453 | return exchange_one_byte; 454 | else 455 | return exchange_bytes; 456 | } 457 | } 458 | 459 | static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) 460 | { 461 | uint8_t *basep = (uint8_t *)base; 462 | size_t i, n, c, r; 463 | exchange_f swap = exchange_func(base, size); 464 | 465 | if (nmemb > 1) { 466 | i = (nmemb / 2) * size; 467 | n = nmemb * size; 468 | 469 | while (i > 0) { 470 | i -= size; 471 | for (r = i; (c = r * 2 + size) < n; r = c) { 472 | if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0) 473 | c += size; 474 | if (cmp(basep + r, basep + c, opaque) > 0) 475 | break; 476 | swap(basep + r, basep + c, size); 477 | } 478 | } 479 | for (i = n - size; i > 0; i -= size) { 480 | swap(basep, basep + i, size); 481 | 482 | for (r = 0; (c = r * 2 + size) < i; r = c) { 483 | if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0) 484 | c += size; 485 | if (cmp(basep + r, basep + c, opaque) > 0) 486 | break; 487 | swap(basep + r, basep + c, size); 488 | } 489 | } 490 | } 491 | } 492 | 493 | static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque) 494 | { 495 | return cmp(a, b, opaque) < 0 ? 496 | (cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) : 497 | (cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c )); 498 | } 499 | 500 | /* pointer based version with local stack and insertion sort threshhold */ 501 | void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) 502 | { 503 | struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack; 504 | uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m; 505 | size_t m4, i, lt, gt, span, span2; 506 | int c, depth; 507 | exchange_f swap = exchange_func(base, size); 508 | exchange_f swap_block = exchange_func(base, size | 128); 509 | 510 | if (nmemb < 2 || size <= 0) 511 | return; 512 | 513 | sp->base = (uint8_t *)base; 514 | sp->count = nmemb; 515 | sp->depth = 0; 516 | sp++; 517 | 518 | while (sp > stack) { 519 | sp--; 520 | ptr = sp->base; 521 | nmemb = sp->count; 522 | depth = sp->depth; 523 | 524 | while (nmemb > 6) { 525 | if (++depth > 50) { 526 | /* depth check to ensure worst case logarithmic time */ 527 | heapsortx(ptr, nmemb, size, cmp, opaque); 528 | nmemb = 0; 529 | break; 530 | } 531 | /* select median of 3 from 1/4, 1/2, 3/4 positions */ 532 | /* should use median of 5 or 9? */ 533 | m4 = (nmemb >> 2) * size; 534 | m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque); 535 | swap(ptr, m, size); /* move the pivot to the start or the array */ 536 | i = lt = 1; 537 | pi = plt = ptr + size; 538 | gt = nmemb; 539 | pj = pgt = top = ptr + nmemb * size; 540 | for (;;) { 541 | while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) { 542 | if (c == 0) { 543 | swap(plt, pi, size); 544 | lt++; 545 | plt += size; 546 | } 547 | i++; 548 | pi += size; 549 | } 550 | while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) { 551 | if (c == 0) { 552 | gt--; 553 | pgt -= size; 554 | swap(pgt, pj, size); 555 | } 556 | } 557 | if (pi >= pj) 558 | break; 559 | swap(pi, pj, size); 560 | i++; 561 | pi += size; 562 | } 563 | /* array has 4 parts: 564 | * from 0 to lt excluded: elements identical to pivot 565 | * from lt to pi excluded: elements smaller than pivot 566 | * from pi to gt excluded: elements greater than pivot 567 | * from gt to n excluded: elements identical to pivot 568 | */ 569 | /* move elements identical to pivot in the middle of the array: */ 570 | /* swap values in ranges [0..lt[ and [i-lt..i[ 571 | swapping the smallest span between lt and i-lt is sufficient 572 | */ 573 | span = plt - ptr; 574 | span2 = pi - plt; 575 | lt = i - lt; 576 | if (span > span2) 577 | span = span2; 578 | swap_block(ptr, pi - span, span); 579 | /* swap values in ranges [gt..top[ and [i..top-(top-gt)[ 580 | swapping the smallest span between top-gt and gt-i is sufficient 581 | */ 582 | span = top - pgt; 583 | span2 = pgt - pi; 584 | pgt = top - span2; 585 | gt = nmemb - (gt - i); 586 | if (span > span2) 587 | span = span2; 588 | swap_block(pi, top - span, span); 589 | 590 | /* now array has 3 parts: 591 | * from 0 to lt excluded: elements smaller than pivot 592 | * from lt to gt excluded: elements identical to pivot 593 | * from gt to n excluded: elements greater than pivot 594 | */ 595 | /* stack the larger segment and keep processing the smaller one 596 | to minimize stack use for pathological distributions */ 597 | if (lt > nmemb - gt) { 598 | sp->base = ptr; 599 | sp->count = lt; 600 | sp->depth = depth; 601 | sp++; 602 | ptr = pgt; 603 | nmemb -= gt; 604 | } else { 605 | sp->base = pgt; 606 | sp->count = nmemb - gt; 607 | sp->depth = depth; 608 | sp++; 609 | nmemb = lt; 610 | } 611 | } 612 | /* Use insertion sort for small fragments */ 613 | for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) { 614 | for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size) 615 | swap(pj, pj - size, size); 616 | } 617 | } 618 | } 619 | 620 | #endif 621 | -------------------------------------------------------------------------------- /cutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * C utilities 3 | * 4 | * Copyright (c) 2017 Fabrice Bellard 5 | * Copyright (c) 2018 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | #ifndef CUTILS_H 26 | #define CUTILS_H 27 | 28 | #include 29 | #include 30 | 31 | /* set if CPU is big endian */ 32 | #undef WORDS_BIGENDIAN 33 | 34 | #define likely(x) __builtin_expect(!!(x), 1) 35 | #define unlikely(x) __builtin_expect(!!(x), 0) 36 | #define force_inline inline __attribute__((always_inline)) 37 | #define no_inline __attribute__((noinline)) 38 | #define __maybe_unused __attribute__((unused)) 39 | 40 | #define xglue(x, y) x ## y 41 | #define glue(x, y) xglue(x, y) 42 | #define stringify(s) tostring(s) 43 | #define tostring(s) #s 44 | 45 | #ifndef offsetof 46 | #define offsetof(type, field) ((size_t) &((type *)0)->field) 47 | #endif 48 | #ifndef countof 49 | #define countof(x) (sizeof(x) / sizeof((x)[0])) 50 | #endif 51 | 52 | typedef int BOOL; 53 | 54 | #ifndef FALSE 55 | enum { 56 | FALSE = 0, 57 | TRUE = 1, 58 | }; 59 | #endif 60 | 61 | void pstrcpy(char *buf, int buf_size, const char *str); 62 | char *pstrcat(char *buf, int buf_size, const char *s); 63 | int strstart(const char *str, const char *val, const char **ptr); 64 | int has_suffix(const char *str, const char *suffix); 65 | 66 | static inline int max_int(int a, int b) 67 | { 68 | if (a > b) 69 | return a; 70 | else 71 | return b; 72 | } 73 | 74 | static inline int min_int(int a, int b) 75 | { 76 | if (a < b) 77 | return a; 78 | else 79 | return b; 80 | } 81 | 82 | static inline uint32_t max_uint32(uint32_t a, uint32_t b) 83 | { 84 | if (a > b) 85 | return a; 86 | else 87 | return b; 88 | } 89 | 90 | static inline uint32_t min_uint32(uint32_t a, uint32_t b) 91 | { 92 | if (a < b) 93 | return a; 94 | else 95 | return b; 96 | } 97 | 98 | static inline int64_t max_int64(int64_t a, int64_t b) 99 | { 100 | if (a > b) 101 | return a; 102 | else 103 | return b; 104 | } 105 | 106 | static inline int64_t min_int64(int64_t a, int64_t b) 107 | { 108 | if (a < b) 109 | return a; 110 | else 111 | return b; 112 | } 113 | 114 | /* WARNING: undefined if a = 0 */ 115 | static inline int clz32(unsigned int a) 116 | { 117 | return __builtin_clz(a); 118 | } 119 | 120 | /* WARNING: undefined if a = 0 */ 121 | static inline int clz64(uint64_t a) 122 | { 123 | return __builtin_clzll(a); 124 | } 125 | 126 | /* WARNING: undefined if a = 0 */ 127 | static inline int ctz32(unsigned int a) 128 | { 129 | return __builtin_ctz(a); 130 | } 131 | 132 | /* WARNING: undefined if a = 0 */ 133 | static inline int ctz64(uint64_t a) 134 | { 135 | return __builtin_ctzll(a); 136 | } 137 | 138 | struct __attribute__((packed)) packed_u64 { 139 | uint64_t v; 140 | }; 141 | 142 | struct __attribute__((packed)) packed_u32 { 143 | uint32_t v; 144 | }; 145 | 146 | struct __attribute__((packed)) packed_u16 { 147 | uint16_t v; 148 | }; 149 | 150 | static inline uint64_t get_u64(const uint8_t *tab) 151 | { 152 | return ((const struct packed_u64 *)tab)->v; 153 | } 154 | 155 | static inline int64_t get_i64(const uint8_t *tab) 156 | { 157 | return (int64_t)((const struct packed_u64 *)tab)->v; 158 | } 159 | 160 | static inline void put_u64(uint8_t *tab, uint64_t val) 161 | { 162 | ((struct packed_u64 *)tab)->v = val; 163 | } 164 | 165 | static inline uint32_t get_u32(const uint8_t *tab) 166 | { 167 | return ((const struct packed_u32 *)tab)->v; 168 | } 169 | 170 | static inline int32_t get_i32(const uint8_t *tab) 171 | { 172 | return (int32_t)((const struct packed_u32 *)tab)->v; 173 | } 174 | 175 | static inline void put_u32(uint8_t *tab, uint32_t val) 176 | { 177 | ((struct packed_u32 *)tab)->v = val; 178 | } 179 | 180 | static inline uint32_t get_u16(const uint8_t *tab) 181 | { 182 | return ((const struct packed_u16 *)tab)->v; 183 | } 184 | 185 | static inline int32_t get_i16(const uint8_t *tab) 186 | { 187 | return (int16_t)((const struct packed_u16 *)tab)->v; 188 | } 189 | 190 | static inline void put_u16(uint8_t *tab, uint16_t val) 191 | { 192 | ((struct packed_u16 *)tab)->v = val; 193 | } 194 | 195 | static inline uint32_t get_u8(const uint8_t *tab) 196 | { 197 | return *tab; 198 | } 199 | 200 | static inline int32_t get_i8(const uint8_t *tab) 201 | { 202 | return (int8_t)*tab; 203 | } 204 | 205 | static inline void put_u8(uint8_t *tab, uint8_t val) 206 | { 207 | *tab = val; 208 | } 209 | 210 | static inline uint16_t bswap16(uint16_t x) 211 | { 212 | return (x >> 8) | (x << 8); 213 | } 214 | 215 | static inline uint32_t bswap32(uint32_t v) 216 | { 217 | return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | 218 | ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); 219 | } 220 | 221 | static inline uint64_t bswap64(uint64_t v) 222 | { 223 | return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | 224 | ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | 225 | ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | 226 | ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | 227 | ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | 228 | ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | 229 | ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | 230 | ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); 231 | } 232 | 233 | /* XXX: should take an extra argument to pass slack information to the caller */ 234 | typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); 235 | 236 | typedef struct DynBuf { 237 | uint8_t *buf; 238 | size_t size; 239 | size_t allocated_size; 240 | BOOL error; /* true if a memory allocation error occurred */ 241 | DynBufReallocFunc *realloc_func; 242 | void *opaque; /* for realloc_func */ 243 | } DynBuf; 244 | 245 | void dbuf_init(DynBuf *s); 246 | void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); 247 | int dbuf_realloc(DynBuf *s, size_t new_size); 248 | int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len); 249 | int dbuf_put(DynBuf *s, const uint8_t *data, size_t len); 250 | int dbuf_put_self(DynBuf *s, size_t offset, size_t len); 251 | int dbuf_putc(DynBuf *s, uint8_t c); 252 | int dbuf_putstr(DynBuf *s, const char *str); 253 | static inline int dbuf_put_u16(DynBuf *s, uint16_t val) 254 | { 255 | return dbuf_put(s, (uint8_t *)&val, 2); 256 | } 257 | static inline int dbuf_put_u32(DynBuf *s, uint32_t val) 258 | { 259 | return dbuf_put(s, (uint8_t *)&val, 4); 260 | } 261 | static inline int dbuf_put_u64(DynBuf *s, uint64_t val) 262 | { 263 | return dbuf_put(s, (uint8_t *)&val, 8); 264 | } 265 | int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, 266 | const char *fmt, ...); 267 | void dbuf_free(DynBuf *s); 268 | static inline BOOL dbuf_error(DynBuf *s) { 269 | return s->error; 270 | } 271 | static inline void dbuf_set_error(DynBuf *s) 272 | { 273 | s->error = TRUE; 274 | } 275 | 276 | #define UTF8_CHAR_LEN_MAX 6 277 | 278 | int unicode_to_utf8(uint8_t *buf, unsigned int c); 279 | int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp); 280 | 281 | static inline int from_hex(int c) 282 | { 283 | if (c >= '0' && c <= '9') 284 | return c - '0'; 285 | else if (c >= 'A' && c <= 'F') 286 | return c - 'A' + 10; 287 | else if (c >= 'a' && c <= 'f') 288 | return c - 'a' + 10; 289 | else 290 | return -1; 291 | } 292 | 293 | void rqsort(void *base, size_t nmemb, size_t size, 294 | int (*cmp)(const void *, const void *, void *), 295 | void *arg); 296 | 297 | #endif /* CUTILS_H */ 298 | -------------------------------------------------------------------------------- /examples/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "fmt" 7 | "github.com/lithdew/quickjs" 8 | "strings" 9 | ) 10 | 11 | func check(err error) { 12 | if err != nil { 13 | var evalErr *quickjs.Error 14 | if errors.As(err, &evalErr) { 15 | fmt.Println(evalErr.Cause) 16 | fmt.Println(evalErr.Stack) 17 | } 18 | panic(err) 19 | } 20 | } 21 | 22 | func main() { 23 | runtime := quickjs.NewRuntime() 24 | defer runtime.Free() 25 | 26 | context := runtime.NewContext() 27 | defer context.Free() 28 | 29 | globals := context.Globals() 30 | 31 | // Test evaluating template strings. 32 | 33 | result, err := context.Eval("`Hello world! 2 ** 8 = ${2 ** 8}.`") 34 | check(err) 35 | defer result.Free() 36 | 37 | fmt.Println(result.String()) 38 | fmt.Println() 39 | 40 | // Test evaluating numeric expressions. 41 | 42 | result, err = context.Eval(`1 + 2 * 100 - 3 + Math.sin(10)`) 43 | check(err) 44 | defer result.Free() 45 | 46 | fmt.Println(result.Int64()) 47 | fmt.Println() 48 | 49 | // Test evaluating big integer expressions. 50 | 51 | result, err = context.Eval(`128n ** 16n`) 52 | check(err) 53 | defer result.Free() 54 | 55 | fmt.Println(result.BigInt()) 56 | fmt.Println() 57 | 58 | // Test evaluating big decimal expressions. 59 | 60 | result, err = context.Eval(`128l ** 12l`) 61 | check(err) 62 | defer result.Free() 63 | 64 | fmt.Println(result.BigFloat()) 65 | fmt.Println() 66 | 67 | // Test evaluating boolean expressions. 68 | 69 | result, err = context.Eval(`false && true`) 70 | check(err) 71 | defer result.Free() 72 | 73 | fmt.Println(result.Bool()) 74 | fmt.Println() 75 | 76 | // Test setting and calling functions. 77 | 78 | A := func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value { 79 | fmt.Println("A got called!") 80 | return ctx.Null() 81 | } 82 | 83 | B := func(ctx *quickjs.Context, this quickjs.Value, args []quickjs.Value) quickjs.Value { 84 | fmt.Println("B got called!") 85 | return ctx.Null() 86 | } 87 | 88 | globals.Set("A", context.Function(A)) 89 | globals.Set("B", context.Function(B)) 90 | 91 | _, err = context.Eval(`for (let i = 0; i < 10; i++) { if (i % 2 === 0) A(); else B(); }`) 92 | check(err) 93 | 94 | fmt.Println() 95 | 96 | // Test setting global variables. 97 | 98 | _, err = context.Eval(`HELLO = "world"; TEST = false;`) 99 | check(err) 100 | 101 | names, err := globals.PropertyNames() 102 | check(err) 103 | 104 | fmt.Println("Globals:") 105 | for _, name := range names { 106 | val := globals.GetByAtom(name.Atom) 107 | defer val.Free() 108 | 109 | fmt.Printf("'%s': %s\n", name, val) 110 | } 111 | fmt.Println() 112 | 113 | // Test evaluating arbitrary expressions from flag arguments. 114 | 115 | flag.Parse() 116 | if flag.NArg() == 0 { 117 | return 118 | } 119 | 120 | result, err = context.Eval(strings.Join(flag.Args(), " ")) 121 | check(err) 122 | defer result.Free() 123 | 124 | if result.IsObject() { 125 | names, err := result.PropertyNames() 126 | check(err) 127 | 128 | fmt.Println("Object:") 129 | for _, name := range names { 130 | val := result.GetByAtom(name.Atom) 131 | defer val.Free() 132 | 133 | fmt.Printf("'%s': %s\n", name, val) 134 | } 135 | } else { 136 | fmt.Println(result.String()) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/elsaland/quickjs 2 | 3 | go 1.14 4 | 5 | require github.com/stretchr/testify v1.6.1 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 8 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 9 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 10 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 11 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 12 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 13 | -------------------------------------------------------------------------------- /libbf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tiny arbitrary precision floating point library 3 | * 4 | * Copyright (c) 2017-2020 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef LIBBF_H 25 | #define LIBBF_H 26 | 27 | #include 28 | #include 29 | 30 | #if defined(__x86_64__) 31 | #define LIMB_LOG2_BITS 6 32 | #else 33 | #define LIMB_LOG2_BITS 5 34 | #endif 35 | 36 | #define LIMB_BITS (1 << LIMB_LOG2_BITS) 37 | 38 | #if LIMB_BITS == 64 39 | typedef __int128 int128_t; 40 | typedef unsigned __int128 uint128_t; 41 | typedef int64_t slimb_t; 42 | typedef uint64_t limb_t; 43 | typedef uint128_t dlimb_t; 44 | #define BF_RAW_EXP_MIN INT64_MIN 45 | #define BF_RAW_EXP_MAX INT64_MAX 46 | 47 | #define LIMB_DIGITS 19 48 | #define BF_DEC_BASE UINT64_C(10000000000000000000) 49 | 50 | #else 51 | 52 | typedef int32_t slimb_t; 53 | typedef uint32_t limb_t; 54 | typedef uint64_t dlimb_t; 55 | #define BF_RAW_EXP_MIN INT32_MIN 56 | #define BF_RAW_EXP_MAX INT32_MAX 57 | 58 | #define LIMB_DIGITS 9 59 | #define BF_DEC_BASE 1000000000U 60 | 61 | #endif 62 | 63 | /* in bits */ 64 | /* minimum number of bits for the exponent */ 65 | #define BF_EXP_BITS_MIN 3 66 | /* maximum number of bits for the exponent */ 67 | #define BF_EXP_BITS_MAX (LIMB_BITS - 3) 68 | /* extended range for exponent, used internally */ 69 | #define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1) 70 | /* minimum possible precision */ 71 | #define BF_PREC_MIN 2 72 | /* minimum possible precision */ 73 | #define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2) 74 | /* some operations support infinite precision */ 75 | #define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ 76 | 77 | #if LIMB_BITS == 64 78 | #define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197)) 79 | #else 80 | #define BF_CHKSUM_MOD 975620677U 81 | #endif 82 | 83 | #define BF_EXP_ZERO BF_RAW_EXP_MIN 84 | #define BF_EXP_INF (BF_RAW_EXP_MAX - 1) 85 | #define BF_EXP_NAN BF_RAW_EXP_MAX 86 | 87 | /* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, 88 | +/-infinity is represented with expn = BF_EXP_INF and len = 0, 89 | NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored) 90 | */ 91 | typedef struct { 92 | struct bf_context_t *ctx; 93 | int sign; 94 | slimb_t expn; 95 | limb_t len; 96 | limb_t *tab; 97 | } bf_t; 98 | 99 | typedef struct { 100 | /* must be kept identical to bf_t */ 101 | struct bf_context_t *ctx; 102 | int sign; 103 | slimb_t expn; 104 | limb_t len; 105 | limb_t *tab; 106 | } bfdec_t; 107 | 108 | typedef enum { 109 | BF_RNDN, /* round to nearest, ties to even */ 110 | BF_RNDZ, /* round to zero */ 111 | BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */ 112 | BF_RNDU, /* round to +inf */ 113 | BF_RNDNA, /* round to nearest, ties away from zero */ 114 | BF_RNDA, /* round away from zero */ 115 | BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, 116 | inexact flag is always set) */ 117 | } bf_rnd_t; 118 | 119 | /* allow subnormal numbers. Only available if the number of exponent 120 | bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */ 121 | #define BF_FLAG_SUBNORMAL (1 << 3) 122 | /* 'prec' is the precision after the radix point instead of the whole 123 | mantissa. Can only be used with bf_round() and 124 | bfdec_[add|sub|mul|div|sqrt|round](). */ 125 | #define BF_FLAG_RADPNT_PREC (1 << 4) 126 | 127 | #define BF_RND_MASK 0x7 128 | #define BF_EXP_BITS_SHIFT 5 129 | #define BF_EXP_BITS_MASK 0x3f 130 | 131 | /* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */ 132 | #define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT) 133 | 134 | /* contains the rounding mode and number of exponents bits */ 135 | typedef uint32_t bf_flags_t; 136 | 137 | typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size); 138 | 139 | typedef struct { 140 | bf_t val; 141 | limb_t prec; 142 | } BFConstCache; 143 | 144 | typedef struct bf_context_t { 145 | void *realloc_opaque; 146 | bf_realloc_func_t *realloc_func; 147 | BFConstCache log2_cache; 148 | BFConstCache pi_cache; 149 | struct BFNTTState *ntt_state; 150 | } bf_context_t; 151 | 152 | static inline int bf_get_exp_bits(bf_flags_t flags) 153 | { 154 | int e; 155 | e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK; 156 | if (e == BF_EXP_BITS_MASK) 157 | return BF_EXP_BITS_MAX + 1; 158 | else 159 | return BF_EXP_BITS_MAX - e; 160 | } 161 | 162 | static inline bf_flags_t bf_set_exp_bits(int n) 163 | { 164 | return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT; 165 | } 166 | 167 | /* returned status */ 168 | #define BF_ST_INVALID_OP (1 << 0) 169 | #define BF_ST_DIVIDE_ZERO (1 << 1) 170 | #define BF_ST_OVERFLOW (1 << 2) 171 | #define BF_ST_UNDERFLOW (1 << 3) 172 | #define BF_ST_INEXACT (1 << 4) 173 | /* indicate that a memory allocation error occured. NaN is returned */ 174 | #define BF_ST_MEM_ERROR (1 << 5) 175 | 176 | #define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */ 177 | 178 | static inline slimb_t bf_max(slimb_t a, slimb_t b) 179 | { 180 | if (a > b) 181 | return a; 182 | else 183 | return b; 184 | } 185 | 186 | static inline slimb_t bf_min(slimb_t a, slimb_t b) 187 | { 188 | if (a < b) 189 | return a; 190 | else 191 | return b; 192 | } 193 | 194 | void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, 195 | void *realloc_opaque); 196 | void bf_context_end(bf_context_t *s); 197 | /* free memory allocated for the bf cache data */ 198 | void bf_clear_cache(bf_context_t *s); 199 | 200 | static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size) 201 | { 202 | return s->realloc_func(s->realloc_opaque, ptr, size); 203 | } 204 | 205 | /* 'size' must be != 0 */ 206 | static inline void *bf_malloc(bf_context_t *s, size_t size) 207 | { 208 | return bf_realloc(s, NULL, size); 209 | } 210 | 211 | static inline void bf_free(bf_context_t *s, void *ptr) 212 | { 213 | /* must test ptr otherwise equivalent to malloc(0) */ 214 | if (ptr) 215 | bf_realloc(s, ptr, 0); 216 | } 217 | 218 | void bf_init(bf_context_t *s, bf_t *r); 219 | 220 | static inline void bf_delete(bf_t *r) 221 | { 222 | bf_context_t *s = r->ctx; 223 | /* we accept to delete a zeroed bf_t structure */ 224 | if (s && r->tab) { 225 | bf_realloc(s, r->tab, 0); 226 | } 227 | } 228 | 229 | static inline void bf_neg(bf_t *r) 230 | { 231 | r->sign ^= 1; 232 | } 233 | 234 | static inline int bf_is_finite(const bf_t *a) 235 | { 236 | return (a->expn < BF_EXP_INF); 237 | } 238 | 239 | static inline int bf_is_nan(const bf_t *a) 240 | { 241 | return (a->expn == BF_EXP_NAN); 242 | } 243 | 244 | static inline int bf_is_zero(const bf_t *a) 245 | { 246 | return (a->expn == BF_EXP_ZERO); 247 | } 248 | 249 | static inline void bf_memcpy(bf_t *r, const bf_t *a) 250 | { 251 | *r = *a; 252 | } 253 | 254 | int bf_set_ui(bf_t *r, uint64_t a); 255 | int bf_set_si(bf_t *r, int64_t a); 256 | void bf_set_nan(bf_t *r); 257 | void bf_set_zero(bf_t *r, int is_neg); 258 | void bf_set_inf(bf_t *r, int is_neg); 259 | int bf_set(bf_t *r, const bf_t *a); 260 | void bf_move(bf_t *r, bf_t *a); 261 | int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode); 262 | int bf_set_float64(bf_t *a, double d); 263 | 264 | int bf_cmpu(const bf_t *a, const bf_t *b); 265 | int bf_cmp_full(const bf_t *a, const bf_t *b); 266 | int bf_cmp(const bf_t *a, const bf_t *b); 267 | static inline int bf_cmp_eq(const bf_t *a, const bf_t *b) 268 | { 269 | return bf_cmp(a, b) == 0; 270 | } 271 | 272 | static inline int bf_cmp_le(const bf_t *a, const bf_t *b) 273 | { 274 | return bf_cmp(a, b) <= 0; 275 | } 276 | 277 | static inline int bf_cmp_lt(const bf_t *a, const bf_t *b) 278 | { 279 | return bf_cmp(a, b) < 0; 280 | } 281 | 282 | int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); 283 | int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); 284 | int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags); 285 | int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); 286 | int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags); 287 | int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, 288 | bf_flags_t flags); 289 | int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags); 290 | int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); 291 | #define BF_DIVREM_EUCLIDIAN BF_RNDF 292 | int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, 293 | limb_t prec, bf_flags_t flags, int rnd_mode); 294 | int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, 295 | bf_flags_t flags, int rnd_mode); 296 | int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, 297 | bf_flags_t flags, int rnd_mode); 298 | /* round to integer with infinite precision */ 299 | int bf_rint(bf_t *r, int rnd_mode); 300 | int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); 301 | int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a); 302 | int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); 303 | slimb_t bf_get_exp_min(const bf_t *a); 304 | int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b); 305 | int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b); 306 | int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b); 307 | 308 | /* additional flags for bf_atof */ 309 | /* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */ 310 | #define BF_ATOF_NO_HEX (1 << 16) 311 | /* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */ 312 | #define BF_ATOF_BIN_OCT (1 << 17) 313 | /* Do not parse NaN or Inf */ 314 | #define BF_ATOF_NO_NAN_INF (1 << 18) 315 | /* return the exponent separately */ 316 | #define BF_ATOF_EXPONENT (1 << 19) 317 | 318 | int bf_atof(bf_t *a, const char *str, const char **pnext, int radix, 319 | limb_t prec, bf_flags_t flags); 320 | /* this version accepts prec = BF_PREC_INF and returns the radix 321 | exponent */ 322 | int bf_atof2(bf_t *r, slimb_t *pexponent, 323 | const char *str, const char **pnext, int radix, 324 | limb_t prec, bf_flags_t flags); 325 | int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, 326 | slimb_t expn, limb_t prec, bf_flags_t flags); 327 | 328 | 329 | /* Conversion of floating point number to string. Return a null 330 | terminated string or NULL if memory error. *plen contains its 331 | length if plen != NULL. The exponent letter is "e" for base 10, 332 | "p" for bases 2, 8, 16 with a binary exponent and "@" for the other 333 | bases. */ 334 | 335 | #define BF_FTOA_FORMAT_MASK (3 << 16) 336 | 337 | /* fixed format: prec significant digits rounded with (flags & 338 | BF_RND_MASK). Exponential notation is used if too many zeros are 339 | needed.*/ 340 | #define BF_FTOA_FORMAT_FIXED (0 << 16) 341 | /* fractional format: prec digits after the decimal point rounded with 342 | (flags & BF_RND_MASK) */ 343 | #define BF_FTOA_FORMAT_FRAC (1 << 16) 344 | /* free format: 345 | 346 | For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum 347 | number of digits to represent 'a'. The precision and the rounding 348 | mode are ignored. 349 | 350 | For the non binary radices with bf_ftoa(): use as many digits as 351 | necessary so that bf_atof() return the same number when using 352 | precision 'prec', rounding to nearest and the subnormal 353 | configuration of 'flags'. The result is meaningful only if 'a' is 354 | already rounded to 'prec' bits. If the subnormal flag is set, the 355 | exponent in 'flags' must also be set to the desired exponent range. 356 | */ 357 | #define BF_FTOA_FORMAT_FREE (2 << 16) 358 | /* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits 359 | (takes more computation time). Identical to BF_FTOA_FORMAT_FREE for 360 | binary radices with bf_ftoa() and for bfdec_ftoa(). */ 361 | #define BF_FTOA_FORMAT_FREE_MIN (3 << 16) 362 | 363 | /* force exponential notation for fixed or free format */ 364 | #define BF_FTOA_FORCE_EXP (1 << 20) 365 | /* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for 366 | base 2 if non zero value */ 367 | #define BF_FTOA_ADD_PREFIX (1 << 21) 368 | /* return "Infinity" instead of "Inf" and add a "+" for positive 369 | exponents */ 370 | #define BF_FTOA_JS_QUIRKS (1 << 22) 371 | 372 | char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, 373 | bf_flags_t flags); 374 | 375 | /* modulo 2^n instead of saturation. NaN and infinity return 0 */ 376 | #define BF_GET_INT_MOD (1 << 0) 377 | int bf_get_int32(int *pres, const bf_t *a, int flags); 378 | int bf_get_int64(int64_t *pres, const bf_t *a, int flags); 379 | int bf_get_uint64(uint64_t *pres, const bf_t *a); 380 | 381 | /* the following functions are exported for testing only. */ 382 | void mp_print_str(const char *str, const limb_t *tab, limb_t n); 383 | void bf_print_str(const char *str, const bf_t *a); 384 | int bf_resize(bf_t *r, limb_t len); 385 | int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len); 386 | int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags); 387 | int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k); 388 | slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, 389 | int is_ceil1); 390 | int mp_mul(bf_context_t *s, limb_t *result, 391 | const limb_t *op1, limb_t op1_size, 392 | const limb_t *op2, limb_t op2_size); 393 | limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, 394 | limb_t n, limb_t carry); 395 | limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n); 396 | int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n); 397 | int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n); 398 | limb_t bf_isqrt(limb_t a); 399 | 400 | /* transcendental functions */ 401 | int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags); 402 | int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags); 403 | int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); 404 | int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); 405 | #define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */ 406 | int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags); 407 | int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); 408 | int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); 409 | int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); 410 | int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); 411 | int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, 412 | limb_t prec, bf_flags_t flags); 413 | int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); 414 | int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); 415 | 416 | /* decimal floating point */ 417 | 418 | static inline void bfdec_init(bf_context_t *s, bfdec_t *r) 419 | { 420 | bf_init(s, (bf_t *)r); 421 | } 422 | static inline void bfdec_delete(bfdec_t *r) 423 | { 424 | bf_delete((bf_t *)r); 425 | } 426 | 427 | static inline void bfdec_neg(bfdec_t *r) 428 | { 429 | r->sign ^= 1; 430 | } 431 | 432 | static inline int bfdec_is_finite(const bfdec_t *a) 433 | { 434 | return (a->expn < BF_EXP_INF); 435 | } 436 | 437 | static inline int bfdec_is_nan(const bfdec_t *a) 438 | { 439 | return (a->expn == BF_EXP_NAN); 440 | } 441 | 442 | static inline int bfdec_is_zero(const bfdec_t *a) 443 | { 444 | return (a->expn == BF_EXP_ZERO); 445 | } 446 | 447 | static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a) 448 | { 449 | bf_memcpy((bf_t *)r, (const bf_t *)a); 450 | } 451 | 452 | int bfdec_set_ui(bfdec_t *r, uint64_t a); 453 | int bfdec_set_si(bfdec_t *r, int64_t a); 454 | 455 | static inline void bfdec_set_nan(bfdec_t *r) 456 | { 457 | bf_set_nan((bf_t *)r); 458 | } 459 | static inline void bfdec_set_zero(bfdec_t *r, int is_neg) 460 | { 461 | bf_set_zero((bf_t *)r, is_neg); 462 | } 463 | static inline void bfdec_set_inf(bfdec_t *r, int is_neg) 464 | { 465 | bf_set_inf((bf_t *)r, is_neg); 466 | } 467 | static inline int bfdec_set(bfdec_t *r, const bfdec_t *a) 468 | { 469 | return bf_set((bf_t *)r, (bf_t *)a); 470 | } 471 | static inline void bfdec_move(bfdec_t *r, bfdec_t *a) 472 | { 473 | bf_move((bf_t *)r, (bf_t *)a); 474 | } 475 | static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b) 476 | { 477 | return bf_cmpu((const bf_t *)a, (const bf_t *)b); 478 | } 479 | static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b) 480 | { 481 | return bf_cmp_full((const bf_t *)a, (const bf_t *)b); 482 | } 483 | static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b) 484 | { 485 | return bf_cmp((const bf_t *)a, (const bf_t *)b); 486 | } 487 | static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b) 488 | { 489 | return bfdec_cmp(a, b) == 0; 490 | } 491 | static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b) 492 | { 493 | return bfdec_cmp(a, b) <= 0; 494 | } 495 | static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b) 496 | { 497 | return bfdec_cmp(a, b) < 0; 498 | } 499 | 500 | int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, 501 | bf_flags_t flags); 502 | int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, 503 | bf_flags_t flags); 504 | int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, 505 | bf_flags_t flags); 506 | int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, 507 | bf_flags_t flags); 508 | int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, 509 | bf_flags_t flags); 510 | int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, 511 | bf_flags_t flags); 512 | int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, 513 | limb_t prec, bf_flags_t flags, int rnd_mode); 514 | int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, 515 | bf_flags_t flags, int rnd_mode); 516 | int bfdec_rint(bfdec_t *r, int rnd_mode); 517 | int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags); 518 | int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags); 519 | int bfdec_get_int32(int *pres, const bfdec_t *a); 520 | int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b); 521 | 522 | char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags); 523 | int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, 524 | limb_t prec, bf_flags_t flags); 525 | 526 | /* the following functions are exported for testing only. */ 527 | extern const limb_t mp_pow_dec[LIMB_DIGITS + 1]; 528 | void bfdec_print_str(const char *str, const bfdec_t *a); 529 | static inline int bfdec_resize(bfdec_t *r, limb_t len) 530 | { 531 | return bf_resize((bf_t *)r, len); 532 | } 533 | int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags); 534 | 535 | #endif /* LIBBF_H */ 536 | -------------------------------------------------------------------------------- /libregexp-opcode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Regular Expression Engine 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #ifdef DEF 26 | 27 | DEF(invalid, 1) /* never used */ 28 | DEF(char, 3) 29 | DEF(char32, 5) 30 | DEF(dot, 1) 31 | DEF(any, 1) /* same as dot but match any character including line terminator */ 32 | DEF(line_start, 1) 33 | DEF(line_end, 1) 34 | DEF(goto, 5) 35 | DEF(split_goto_first, 5) 36 | DEF(split_next_first, 5) 37 | DEF(match, 1) 38 | DEF(save_start, 2) /* save start position */ 39 | DEF(save_end, 2) /* save end position, must come after saved_start */ 40 | DEF(save_reset, 3) /* reset save positions */ 41 | DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ 42 | DEF(push_i32, 5) /* push integer on the stack */ 43 | DEF(drop, 1) 44 | DEF(word_boundary, 1) 45 | DEF(not_word_boundary, 1) 46 | DEF(back_reference, 2) 47 | DEF(backward_back_reference, 2) /* must come after back_reference */ 48 | DEF(range, 3) /* variable length */ 49 | DEF(range32, 3) /* variable length */ 50 | DEF(lookahead, 5) 51 | DEF(negative_lookahead, 5) 52 | DEF(push_char_pos, 1) /* push the character position on the stack */ 53 | DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character 54 | position */ 55 | DEF(prev, 1) /* go to the previous char */ 56 | DEF(simple_greedy_quant, 17) 57 | 58 | #endif /* DEF */ 59 | -------------------------------------------------------------------------------- /libregexp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Regular Expression Engine 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef LIBREGEXP_H 25 | #define LIBREGEXP_H 26 | 27 | #include 28 | 29 | #include "libunicode.h" 30 | 31 | #define LRE_BOOL int /* for documentation purposes */ 32 | 33 | #define LRE_FLAG_GLOBAL (1 << 0) 34 | #define LRE_FLAG_IGNORECASE (1 << 1) 35 | #define LRE_FLAG_MULTILINE (1 << 2) 36 | #define LRE_FLAG_DOTALL (1 << 3) 37 | #define LRE_FLAG_UTF16 (1 << 4) 38 | #define LRE_FLAG_STICKY (1 << 5) 39 | 40 | #define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ 41 | 42 | uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, 43 | const char *buf, size_t buf_len, int re_flags, 44 | void *opaque); 45 | int lre_get_capture_count(const uint8_t *bc_buf); 46 | int lre_get_flags(const uint8_t *bc_buf); 47 | int lre_exec(uint8_t **capture, 48 | const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, 49 | int cbuf_type, void *opaque); 50 | 51 | int lre_parse_escape(const uint8_t **pp, int allow_utf16); 52 | LRE_BOOL lre_is_space(int c); 53 | 54 | /* must be provided by the user */ 55 | LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); 56 | void *lre_realloc(void *opaque, void *ptr, size_t size); 57 | 58 | /* JS identifier test */ 59 | extern uint32_t const lre_id_start_table_ascii[4]; 60 | extern uint32_t const lre_id_continue_table_ascii[4]; 61 | 62 | static inline int lre_js_is_ident_first(int c) 63 | { 64 | if ((uint32_t)c < 128) { 65 | return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; 66 | } else { 67 | #ifdef CONFIG_ALL_UNICODE 68 | return lre_is_id_start(c); 69 | #else 70 | return !lre_is_space(c); 71 | #endif 72 | } 73 | } 74 | 75 | static inline int lre_js_is_ident_next(int c) 76 | { 77 | if ((uint32_t)c < 128) { 78 | return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; 79 | } else { 80 | /* ZWNJ and ZWJ are accepted in identifiers */ 81 | #ifdef CONFIG_ALL_UNICODE 82 | return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; 83 | #else 84 | return !lre_is_space(c) || c == 0x200C || c == 0x200D; 85 | #endif 86 | } 87 | } 88 | 89 | #undef LRE_BOOL 90 | 91 | #endif /* LIBREGEXP_H */ 92 | -------------------------------------------------------------------------------- /libunicode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Unicode utilities 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "cutils.h" 31 | #include "libunicode.h" 32 | #include "libunicode-table.h" 33 | 34 | enum { 35 | RUN_TYPE_U, 36 | RUN_TYPE_L, 37 | RUN_TYPE_UF, 38 | RUN_TYPE_LF, 39 | RUN_TYPE_UL, 40 | RUN_TYPE_LSU, 41 | RUN_TYPE_U2L_399_EXT2, 42 | RUN_TYPE_UF_D20, 43 | RUN_TYPE_UF_D1_EXT, 44 | RUN_TYPE_U_EXT, 45 | RUN_TYPE_LF_EXT, 46 | RUN_TYPE_U_EXT2, 47 | RUN_TYPE_L_EXT2, 48 | RUN_TYPE_U_EXT3, 49 | }; 50 | 51 | /* conv_type: 52 | 0 = to upper 53 | 1 = to lower 54 | 2 = case folding (= to lower with modifications) 55 | */ 56 | int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) 57 | { 58 | if (c < 128) { 59 | if (conv_type) { 60 | if (c >= 'A' && c <= 'Z') { 61 | c = c - 'A' + 'a'; 62 | } 63 | } else { 64 | if (c >= 'a' && c <= 'z') { 65 | c = c - 'a' + 'A'; 66 | } 67 | } 68 | } else { 69 | uint32_t v, code, data, type, len, a, is_lower; 70 | int idx, idx_min, idx_max; 71 | 72 | is_lower = (conv_type != 0); 73 | idx_min = 0; 74 | idx_max = countof(case_conv_table1) - 1; 75 | while (idx_min <= idx_max) { 76 | idx = (unsigned)(idx_max + idx_min) / 2; 77 | v = case_conv_table1[idx]; 78 | code = v >> (32 - 17); 79 | len = (v >> (32 - 17 - 7)) & 0x7f; 80 | if (c < code) { 81 | idx_max = idx - 1; 82 | } else if (c >= code + len) { 83 | idx_min = idx + 1; 84 | } else { 85 | type = (v >> (32 - 17 - 7 - 4)) & 0xf; 86 | data = ((v & 0xf) << 8) | case_conv_table2[idx]; 87 | switch(type) { 88 | case RUN_TYPE_U: 89 | case RUN_TYPE_L: 90 | case RUN_TYPE_UF: 91 | case RUN_TYPE_LF: 92 | if (conv_type == (type & 1) || 93 | (type >= RUN_TYPE_UF && conv_type == 2)) { 94 | c = c - code + (case_conv_table1[data] >> (32 - 17)); 95 | } 96 | break; 97 | case RUN_TYPE_UL: 98 | a = c - code; 99 | if ((a & 1) != (1 - is_lower)) 100 | break; 101 | c = (a ^ 1) + code; 102 | break; 103 | case RUN_TYPE_LSU: 104 | a = c - code; 105 | if (a == 1) { 106 | c += 2 * is_lower - 1; 107 | } else if (a == (1 - is_lower) * 2) { 108 | c += (2 * is_lower - 1) * 2; 109 | } 110 | break; 111 | case RUN_TYPE_U2L_399_EXT2: 112 | if (!is_lower) { 113 | res[0] = c - code + case_conv_ext[data >> 6]; 114 | res[1] = 0x399; 115 | return 2; 116 | } else { 117 | c = c - code + case_conv_ext[data & 0x3f]; 118 | } 119 | break; 120 | case RUN_TYPE_UF_D20: 121 | if (conv_type == 1) 122 | break; 123 | c = data + (conv_type == 2) * 0x20; 124 | break; 125 | case RUN_TYPE_UF_D1_EXT: 126 | if (conv_type == 1) 127 | break; 128 | c = case_conv_ext[data] + (conv_type == 2); 129 | break; 130 | case RUN_TYPE_U_EXT: 131 | case RUN_TYPE_LF_EXT: 132 | if (is_lower != (type - RUN_TYPE_U_EXT)) 133 | break; 134 | c = case_conv_ext[data]; 135 | break; 136 | case RUN_TYPE_U_EXT2: 137 | case RUN_TYPE_L_EXT2: 138 | if (conv_type != (type - RUN_TYPE_U_EXT2)) 139 | break; 140 | res[0] = c - code + case_conv_ext[data >> 6]; 141 | res[1] = case_conv_ext[data & 0x3f]; 142 | return 2; 143 | default: 144 | case RUN_TYPE_U_EXT3: 145 | if (conv_type != 0) 146 | break; 147 | res[0] = case_conv_ext[data >> 8]; 148 | res[1] = case_conv_ext[(data >> 4) & 0xf]; 149 | res[2] = case_conv_ext[data & 0xf]; 150 | return 3; 151 | } 152 | break; 153 | } 154 | } 155 | } 156 | res[0] = c; 157 | return 1; 158 | } 159 | 160 | static uint32_t get_le24(const uint8_t *ptr) 161 | { 162 | #if defined(__x86__) || defined(__x86_64__) 163 | return *(uint16_t *)ptr | (ptr[2] << 16); 164 | #else 165 | return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); 166 | #endif 167 | } 168 | 169 | #define UNICODE_INDEX_BLOCK_LEN 32 170 | 171 | /* return -1 if not in table, otherwise the offset in the block */ 172 | static int get_index_pos(uint32_t *pcode, uint32_t c, 173 | const uint8_t *index_table, int index_table_len) 174 | { 175 | uint32_t code, v; 176 | int idx_min, idx_max, idx; 177 | 178 | idx_min = 0; 179 | v = get_le24(index_table); 180 | code = v & ((1 << 21) - 1); 181 | if (c < code) { 182 | *pcode = 0; 183 | return 0; 184 | } 185 | idx_max = index_table_len - 1; 186 | code = get_le24(index_table + idx_max * 3); 187 | if (c >= code) 188 | return -1; 189 | /* invariant: tab[idx_min] <= c < tab2[idx_max] */ 190 | while ((idx_max - idx_min) > 1) { 191 | idx = (idx_max + idx_min) / 2; 192 | v = get_le24(index_table + idx * 3); 193 | code = v & ((1 << 21) - 1); 194 | if (c < code) { 195 | idx_max = idx; 196 | } else { 197 | idx_min = idx; 198 | } 199 | } 200 | v = get_le24(index_table + idx_min * 3); 201 | *pcode = v & ((1 << 21) - 1); 202 | return (idx_min + 1) * UNICODE_INDEX_BLOCK_LEN + (v >> 21); 203 | } 204 | 205 | static BOOL lre_is_in_table(uint32_t c, const uint8_t *table, 206 | const uint8_t *index_table, int index_table_len) 207 | { 208 | uint32_t code, b, bit; 209 | int pos; 210 | const uint8_t *p; 211 | 212 | pos = get_index_pos(&code, c, index_table, index_table_len); 213 | if (pos < 0) 214 | return FALSE; /* outside the table */ 215 | p = table + pos; 216 | bit = 0; 217 | for(;;) { 218 | b = *p++; 219 | if (b < 64) { 220 | code += (b >> 3) + 1; 221 | if (c < code) 222 | return bit; 223 | bit ^= 1; 224 | code += (b & 7) + 1; 225 | } else if (b >= 0x80) { 226 | code += b - 0x80 + 1; 227 | } else if (b < 0x60) { 228 | code += (((b - 0x40) << 8) | p[0]) + 1; 229 | p++; 230 | } else { 231 | code += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; 232 | p += 2; 233 | } 234 | if (c < code) 235 | return bit; 236 | bit ^= 1; 237 | } 238 | } 239 | 240 | BOOL lre_is_cased(uint32_t c) 241 | { 242 | uint32_t v, code, len; 243 | int idx, idx_min, idx_max; 244 | 245 | idx_min = 0; 246 | idx_max = countof(case_conv_table1) - 1; 247 | while (idx_min <= idx_max) { 248 | idx = (unsigned)(idx_max + idx_min) / 2; 249 | v = case_conv_table1[idx]; 250 | code = v >> (32 - 17); 251 | len = (v >> (32 - 17 - 7)) & 0x7f; 252 | if (c < code) { 253 | idx_max = idx - 1; 254 | } else if (c >= code + len) { 255 | idx_min = idx + 1; 256 | } else { 257 | return TRUE; 258 | } 259 | } 260 | return lre_is_in_table(c, unicode_prop_Cased1_table, 261 | unicode_prop_Cased1_index, 262 | sizeof(unicode_prop_Cased1_index) / 3); 263 | } 264 | 265 | BOOL lre_is_case_ignorable(uint32_t c) 266 | { 267 | return lre_is_in_table(c, unicode_prop_Case_Ignorable_table, 268 | unicode_prop_Case_Ignorable_index, 269 | sizeof(unicode_prop_Case_Ignorable_index) / 3); 270 | } 271 | 272 | /* character range */ 273 | 274 | static __maybe_unused void cr_dump(CharRange *cr) 275 | { 276 | int i; 277 | for(i = 0; i < cr->len; i++) 278 | printf("%d: 0x%04x\n", i, cr->points[i]); 279 | } 280 | 281 | static void *cr_default_realloc(void *opaque, void *ptr, size_t size) 282 | { 283 | return realloc(ptr, size); 284 | } 285 | 286 | void cr_init(CharRange *cr, void *mem_opaque, DynBufReallocFunc *realloc_func) 287 | { 288 | cr->len = cr->size = 0; 289 | cr->points = NULL; 290 | cr->mem_opaque = mem_opaque; 291 | cr->realloc_func = realloc_func ? realloc_func : cr_default_realloc; 292 | } 293 | 294 | void cr_free(CharRange *cr) 295 | { 296 | cr->realloc_func(cr->mem_opaque, cr->points, 0); 297 | } 298 | 299 | int cr_realloc(CharRange *cr, int size) 300 | { 301 | int new_size; 302 | uint32_t *new_buf; 303 | 304 | if (size > cr->size) { 305 | new_size = max_int(size, cr->size * 3 / 2); 306 | new_buf = cr->realloc_func(cr->mem_opaque, cr->points, 307 | new_size * sizeof(cr->points[0])); 308 | if (!new_buf) 309 | return -1; 310 | cr->points = new_buf; 311 | cr->size = new_size; 312 | } 313 | return 0; 314 | } 315 | 316 | int cr_copy(CharRange *cr, const CharRange *cr1) 317 | { 318 | if (cr_realloc(cr, cr1->len)) 319 | return -1; 320 | memcpy(cr->points, cr1->points, sizeof(cr->points[0]) * cr1->len); 321 | cr->len = cr1->len; 322 | return 0; 323 | } 324 | 325 | /* merge consecutive intervals and remove empty intervals */ 326 | static void cr_compress(CharRange *cr) 327 | { 328 | int i, j, k, len; 329 | uint32_t *pt; 330 | 331 | pt = cr->points; 332 | len = cr->len; 333 | i = 0; 334 | j = 0; 335 | k = 0; 336 | while ((i + 1) < len) { 337 | if (pt[i] == pt[i + 1]) { 338 | /* empty interval */ 339 | i += 2; 340 | } else { 341 | j = i; 342 | while ((j + 3) < len && pt[j + 1] == pt[j + 2]) 343 | j += 2; 344 | /* just copy */ 345 | pt[k] = pt[i]; 346 | pt[k + 1] = pt[j + 1]; 347 | k += 2; 348 | i = j + 2; 349 | } 350 | } 351 | cr->len = k; 352 | } 353 | 354 | /* union or intersection */ 355 | int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, 356 | const uint32_t *b_pt, int b_len, int op) 357 | { 358 | int a_idx, b_idx, is_in; 359 | uint32_t v; 360 | 361 | a_idx = 0; 362 | b_idx = 0; 363 | for(;;) { 364 | /* get one more point from a or b in increasing order */ 365 | if (a_idx < a_len && b_idx < b_len) { 366 | if (a_pt[a_idx] < b_pt[b_idx]) { 367 | goto a_add; 368 | } else if (a_pt[a_idx] == b_pt[b_idx]) { 369 | v = a_pt[a_idx]; 370 | a_idx++; 371 | b_idx++; 372 | } else { 373 | goto b_add; 374 | } 375 | } else if (a_idx < a_len) { 376 | a_add: 377 | v = a_pt[a_idx++]; 378 | } else if (b_idx < b_len) { 379 | b_add: 380 | v = b_pt[b_idx++]; 381 | } else { 382 | break; 383 | } 384 | /* add the point if the in/out status changes */ 385 | switch(op) { 386 | case CR_OP_UNION: 387 | is_in = (a_idx & 1) | (b_idx & 1); 388 | break; 389 | case CR_OP_INTER: 390 | is_in = (a_idx & 1) & (b_idx & 1); 391 | break; 392 | case CR_OP_XOR: 393 | is_in = (a_idx & 1) ^ (b_idx & 1); 394 | break; 395 | default: 396 | abort(); 397 | } 398 | if (is_in != (cr->len & 1)) { 399 | if (cr_add_point(cr, v)) 400 | return -1; 401 | } 402 | } 403 | cr_compress(cr); 404 | return 0; 405 | } 406 | 407 | int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len) 408 | { 409 | CharRange a = *cr; 410 | int ret; 411 | cr->len = 0; 412 | cr->size = 0; 413 | cr->points = NULL; 414 | ret = cr_op(cr, a.points, a.len, b_pt, b_len, CR_OP_UNION); 415 | cr_free(&a); 416 | return ret; 417 | } 418 | 419 | int cr_invert(CharRange *cr) 420 | { 421 | int len; 422 | len = cr->len; 423 | if (cr_realloc(cr, len + 2)) 424 | return -1; 425 | memmove(cr->points + 1, cr->points, len * sizeof(cr->points[0])); 426 | cr->points[0] = 0; 427 | cr->points[len + 1] = UINT32_MAX; 428 | cr->len = len + 2; 429 | cr_compress(cr); 430 | return 0; 431 | } 432 | 433 | #ifdef CONFIG_ALL_UNICODE 434 | 435 | BOOL lre_is_id_start(uint32_t c) 436 | { 437 | return lre_is_in_table(c, unicode_prop_ID_Start_table, 438 | unicode_prop_ID_Start_index, 439 | sizeof(unicode_prop_ID_Start_index) / 3); 440 | } 441 | 442 | BOOL lre_is_id_continue(uint32_t c) 443 | { 444 | return lre_is_id_start(c) || 445 | lre_is_in_table(c, unicode_prop_ID_Continue1_table, 446 | unicode_prop_ID_Continue1_index, 447 | sizeof(unicode_prop_ID_Continue1_index) / 3); 448 | } 449 | 450 | #define UNICODE_DECOMP_LEN_MAX 18 451 | 452 | typedef enum { 453 | DECOMP_TYPE_C1, /* 16 bit char */ 454 | DECOMP_TYPE_L1, /* 16 bit char table */ 455 | DECOMP_TYPE_L2, 456 | DECOMP_TYPE_L3, 457 | DECOMP_TYPE_L4, 458 | DECOMP_TYPE_L5, /* XXX: not used */ 459 | DECOMP_TYPE_L6, /* XXX: could remove */ 460 | DECOMP_TYPE_L7, /* XXX: could remove */ 461 | DECOMP_TYPE_LL1, /* 18 bit char table */ 462 | DECOMP_TYPE_LL2, 463 | DECOMP_TYPE_S1, /* 8 bit char table */ 464 | DECOMP_TYPE_S2, 465 | DECOMP_TYPE_S3, 466 | DECOMP_TYPE_S4, 467 | DECOMP_TYPE_S5, 468 | DECOMP_TYPE_I1, /* increment 16 bit char value */ 469 | DECOMP_TYPE_I2_0, 470 | DECOMP_TYPE_I2_1, 471 | DECOMP_TYPE_I3_1, 472 | DECOMP_TYPE_I3_2, 473 | DECOMP_TYPE_I4_1, 474 | DECOMP_TYPE_I4_2, 475 | DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */ 476 | DECOMP_TYPE_B2, 477 | DECOMP_TYPE_B3, 478 | DECOMP_TYPE_B4, 479 | DECOMP_TYPE_B5, 480 | DECOMP_TYPE_B6, 481 | DECOMP_TYPE_B7, 482 | DECOMP_TYPE_B8, 483 | DECOMP_TYPE_B18, 484 | DECOMP_TYPE_LS2, 485 | DECOMP_TYPE_PAT3, 486 | DECOMP_TYPE_S2_UL, 487 | DECOMP_TYPE_LS2_UL, 488 | } DecompTypeEnum; 489 | 490 | static uint32_t unicode_get_short_code(uint32_t c) 491 | { 492 | static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 }; 493 | 494 | if (c < 0x80) 495 | return c; 496 | else if (c < 0x80 + 0x50) 497 | return c - 0x80 + 0x300; 498 | else 499 | return unicode_short_table[c - 0x80 - 0x50]; 500 | } 501 | 502 | static uint32_t unicode_get_lower_simple(uint32_t c) 503 | { 504 | if (c < 0x100 || (c >= 0x410 && c <= 0x42f)) 505 | c += 0x20; 506 | else 507 | c++; 508 | return c; 509 | } 510 | 511 | static uint16_t unicode_get16(const uint8_t *p) 512 | { 513 | return p[0] | (p[1] << 8); 514 | } 515 | 516 | static int unicode_decomp_entry(uint32_t *res, uint32_t c, 517 | int idx, uint32_t code, uint32_t len, 518 | uint32_t type) 519 | { 520 | uint32_t c1; 521 | int l, i, p; 522 | const uint8_t *d; 523 | 524 | if (type == DECOMP_TYPE_C1) { 525 | res[0] = unicode_decomp_table2[idx]; 526 | return 1; 527 | } else { 528 | d = unicode_decomp_data + unicode_decomp_table2[idx]; 529 | switch(type) { 530 | case DECOMP_TYPE_L1 ... DECOMP_TYPE_L7: 531 | l = type - DECOMP_TYPE_L1 + 1; 532 | d += (c - code) * l * 2; 533 | for(i = 0; i < l; i++) { 534 | if ((res[i] = unicode_get16(d + 2 * i)) == 0) 535 | return 0; 536 | } 537 | return l; 538 | case DECOMP_TYPE_LL1 ... DECOMP_TYPE_LL2: 539 | { 540 | uint32_t k, p; 541 | l = type - DECOMP_TYPE_LL1 + 1; 542 | k = (c - code) * l; 543 | p = len * l * 2; 544 | for(i = 0; i < l; i++) { 545 | c1 = unicode_get16(d + 2 * k) | 546 | (((d[p + (k / 4)] >> ((k % 4) * 2)) & 3) << 16); 547 | if (!c1) 548 | return 0; 549 | res[i] = c1; 550 | k++; 551 | } 552 | } 553 | return l; 554 | case DECOMP_TYPE_S1 ... DECOMP_TYPE_S5: 555 | l = type - DECOMP_TYPE_S1 + 1; 556 | d += (c - code) * l; 557 | for(i = 0; i < l; i++) { 558 | if ((res[i] = unicode_get_short_code(d[i])) == 0) 559 | return 0; 560 | } 561 | return l; 562 | case DECOMP_TYPE_I1: 563 | l = 1; 564 | p = 0; 565 | goto decomp_type_i; 566 | case DECOMP_TYPE_I2_0: 567 | case DECOMP_TYPE_I2_1: 568 | case DECOMP_TYPE_I3_1: 569 | case DECOMP_TYPE_I3_2: 570 | case DECOMP_TYPE_I4_1: 571 | case DECOMP_TYPE_I4_2: 572 | l = 2 + ((type - DECOMP_TYPE_I2_0) >> 1); 573 | p = ((type - DECOMP_TYPE_I2_0) & 1) + (l > 2); 574 | decomp_type_i: 575 | for(i = 0; i < l; i++) { 576 | c1 = unicode_get16(d + 2 * i); 577 | if (i == p) 578 | c1 += c - code; 579 | res[i] = c1; 580 | } 581 | return l; 582 | case DECOMP_TYPE_B18: 583 | l = 18; 584 | goto decomp_type_b; 585 | case DECOMP_TYPE_B1 ... DECOMP_TYPE_B8: 586 | l = type - DECOMP_TYPE_B1 + 1; 587 | decomp_type_b: 588 | { 589 | uint32_t c_min; 590 | c_min = unicode_get16(d); 591 | d += 2 + (c - code) * l; 592 | for(i = 0; i < l; i++) { 593 | c1 = d[i]; 594 | if (c1 == 0xff) 595 | c1 = 0x20; 596 | else 597 | c1 += c_min; 598 | res[i] = c1; 599 | } 600 | } 601 | return l; 602 | case DECOMP_TYPE_LS2: 603 | d += (c - code) * 3; 604 | if (!(res[0] = unicode_get16(d))) 605 | return 0; 606 | res[1] = unicode_get_short_code(d[2]); 607 | return 2; 608 | case DECOMP_TYPE_PAT3: 609 | res[0] = unicode_get16(d); 610 | res[2] = unicode_get16(d + 2); 611 | d += 4 + (c - code) * 2; 612 | res[1] = unicode_get16(d); 613 | return 3; 614 | case DECOMP_TYPE_S2_UL: 615 | case DECOMP_TYPE_LS2_UL: 616 | c1 = c - code; 617 | if (type == DECOMP_TYPE_S2_UL) { 618 | d += c1 & ~1; 619 | c = unicode_get_short_code(*d); 620 | d++; 621 | } else { 622 | d += (c1 >> 1) * 3; 623 | c = unicode_get16(d); 624 | d += 2; 625 | } 626 | if (c1 & 1) 627 | c = unicode_get_lower_simple(c); 628 | res[0] = c; 629 | res[1] = unicode_get_short_code(*d); 630 | return 2; 631 | } 632 | } 633 | return 0; 634 | } 635 | 636 | 637 | /* return the length of the decomposition (length <= 638 | UNICODE_DECOMP_LEN_MAX) or 0 if no decomposition */ 639 | static int unicode_decomp_char(uint32_t *res, uint32_t c, BOOL is_compat1) 640 | { 641 | uint32_t v, type, is_compat, code, len; 642 | int idx_min, idx_max, idx; 643 | 644 | idx_min = 0; 645 | idx_max = countof(unicode_decomp_table1) - 1; 646 | while (idx_min <= idx_max) { 647 | idx = (idx_max + idx_min) / 2; 648 | v = unicode_decomp_table1[idx]; 649 | code = v >> (32 - 18); 650 | len = (v >> (32 - 18 - 7)) & 0x7f; 651 | // printf("idx=%d code=%05x len=%d\n", idx, code, len); 652 | if (c < code) { 653 | idx_max = idx - 1; 654 | } else if (c >= code + len) { 655 | idx_min = idx + 1; 656 | } else { 657 | is_compat = v & 1; 658 | if (is_compat1 < is_compat) 659 | break; 660 | type = (v >> (32 - 18 - 7 - 6)) & 0x3f; 661 | return unicode_decomp_entry(res, c, idx, code, len, type); 662 | } 663 | } 664 | return 0; 665 | } 666 | 667 | /* return 0 if no pair found */ 668 | static int unicode_compose_pair(uint32_t c0, uint32_t c1) 669 | { 670 | uint32_t code, len, type, v, idx1, d_idx, d_offset, ch; 671 | int idx_min, idx_max, idx, d; 672 | uint32_t pair[2]; 673 | 674 | idx_min = 0; 675 | idx_max = countof(unicode_comp_table) - 1; 676 | while (idx_min <= idx_max) { 677 | idx = (idx_max + idx_min) / 2; 678 | idx1 = unicode_comp_table[idx]; 679 | 680 | /* idx1 represent an entry of the decomposition table */ 681 | d_idx = idx1 >> 6; 682 | d_offset = idx1 & 0x3f; 683 | v = unicode_decomp_table1[d_idx]; 684 | code = v >> (32 - 18); 685 | len = (v >> (32 - 18 - 7)) & 0x7f; 686 | type = (v >> (32 - 18 - 7 - 6)) & 0x3f; 687 | ch = code + d_offset; 688 | unicode_decomp_entry(pair, ch, d_idx, code, len, type); 689 | d = c0 - pair[0]; 690 | if (d == 0) 691 | d = c1 - pair[1]; 692 | if (d < 0) { 693 | idx_max = idx - 1; 694 | } else if (d > 0) { 695 | idx_min = idx + 1; 696 | } else { 697 | return ch; 698 | } 699 | } 700 | return 0; 701 | } 702 | 703 | /* return the combining class of character c (between 0 and 255) */ 704 | static int unicode_get_cc(uint32_t c) 705 | { 706 | uint32_t code, n, type, cc, c1, b; 707 | int pos; 708 | const uint8_t *p; 709 | 710 | pos = get_index_pos(&code, c, 711 | unicode_cc_index, sizeof(unicode_cc_index) / 3); 712 | if (pos < 0) 713 | return 0; 714 | p = unicode_cc_table + pos; 715 | for(;;) { 716 | b = *p++; 717 | type = b >> 6; 718 | n = b & 0x3f; 719 | if (n < 48) { 720 | } else if (n < 56) { 721 | n = (n - 48) << 8; 722 | n |= *p++; 723 | n += 48; 724 | } else { 725 | n = (n - 56) << 8; 726 | n |= *p++ << 8; 727 | n |= *p++; 728 | n += 48 + (1 << 11); 729 | } 730 | if (type <= 1) 731 | p++; 732 | c1 = code + n + 1; 733 | if (c < c1) { 734 | switch(type) { 735 | case 0: 736 | cc = p[-1]; 737 | break; 738 | case 1: 739 | cc = p[-1] + c - code; 740 | break; 741 | case 2: 742 | cc = 0; 743 | break; 744 | default: 745 | case 3: 746 | cc = 230; 747 | break; 748 | } 749 | return cc; 750 | } 751 | code = c1; 752 | } 753 | } 754 | 755 | static void sort_cc(int *buf, int len) 756 | { 757 | int i, j, k, cc, cc1, start, ch1; 758 | 759 | for(i = 0; i < len; i++) { 760 | cc = unicode_get_cc(buf[i]); 761 | if (cc != 0) { 762 | start = i; 763 | j = i + 1; 764 | while (j < len) { 765 | ch1 = buf[j]; 766 | cc1 = unicode_get_cc(ch1); 767 | if (cc1 == 0) 768 | break; 769 | k = j - 1; 770 | while (k >= start) { 771 | if (unicode_get_cc(buf[k]) <= cc1) 772 | break; 773 | buf[k + 1] = buf[k]; 774 | k--; 775 | } 776 | buf[k + 1] = ch1; 777 | j++; 778 | } 779 | #if 0 780 | printf("cc:"); 781 | for(k = start; k < j; k++) { 782 | printf(" %3d", unicode_get_cc(buf[k])); 783 | } 784 | printf("\n"); 785 | #endif 786 | i = j; 787 | } 788 | } 789 | } 790 | 791 | static void to_nfd_rec(DynBuf *dbuf, 792 | const int *src, int src_len, int is_compat) 793 | { 794 | uint32_t c, v; 795 | int i, l; 796 | uint32_t res[UNICODE_DECOMP_LEN_MAX]; 797 | 798 | for(i = 0; i < src_len; i++) { 799 | c = src[i]; 800 | if (c >= 0xac00 && c < 0xd7a4) { 801 | /* Hangul decomposition */ 802 | c -= 0xac00; 803 | dbuf_put_u32(dbuf, 0x1100 + c / 588); 804 | dbuf_put_u32(dbuf, 0x1161 + (c % 588) / 28); 805 | v = c % 28; 806 | if (v != 0) 807 | dbuf_put_u32(dbuf, 0x11a7 + v); 808 | } else { 809 | l = unicode_decomp_char(res, c, is_compat); 810 | if (l) { 811 | to_nfd_rec(dbuf, (int *)res, l, is_compat); 812 | } else { 813 | dbuf_put_u32(dbuf, c); 814 | } 815 | } 816 | } 817 | } 818 | 819 | /* return 0 if not found */ 820 | static int compose_pair(uint32_t c0, uint32_t c1) 821 | { 822 | /* Hangul composition */ 823 | if (c0 >= 0x1100 && c0 < 0x1100 + 19 && 824 | c1 >= 0x1161 && c1 < 0x1161 + 21) { 825 | return 0xac00 + (c0 - 0x1100) * 588 + (c1 - 0x1161) * 28; 826 | } else if (c0 >= 0xac00 && c0 < 0xac00 + 11172 && 827 | (c0 - 0xac00) % 28 == 0 && 828 | c1 >= 0x11a7 && c1 < 0x11a7 + 28) { 829 | return c0 + c1 - 0x11a7; 830 | } else { 831 | return unicode_compose_pair(c0, c1); 832 | } 833 | } 834 | 835 | int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, 836 | UnicodeNormalizationEnum n_type, 837 | void *opaque, DynBufReallocFunc *realloc_func) 838 | { 839 | int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len; 840 | BOOL is_compat; 841 | DynBuf dbuf_s, *dbuf = &dbuf_s; 842 | 843 | is_compat = n_type >> 1; 844 | 845 | dbuf_init2(dbuf, opaque, realloc_func); 846 | if (dbuf_realloc(dbuf, sizeof(int) * src_len)) 847 | goto fail; 848 | 849 | /* common case: latin1 is unaffected by NFC */ 850 | if (n_type == UNICODE_NFC) { 851 | for(i = 0; i < src_len; i++) { 852 | if (src[i] >= 0x100) 853 | goto not_latin1; 854 | } 855 | buf = (int *)dbuf->buf; 856 | memcpy(buf, src, src_len * sizeof(int)); 857 | *pdst = (uint32_t *)buf; 858 | return src_len; 859 | not_latin1: ; 860 | } 861 | 862 | to_nfd_rec(dbuf, (const int *)src, src_len, is_compat); 863 | if (dbuf_error(dbuf)) { 864 | fail: 865 | *pdst = NULL; 866 | return -1; 867 | } 868 | buf = (int *)dbuf->buf; 869 | buf_len = dbuf->size / sizeof(int); 870 | 871 | sort_cc(buf, buf_len); 872 | 873 | if (buf_len <= 1 || (n_type & 1) != 0) { 874 | /* NFD / NFKD */ 875 | *pdst = (uint32_t *)buf; 876 | return buf_len; 877 | } 878 | 879 | i = 1; 880 | out_len = 1; 881 | while (i < buf_len) { 882 | /* find the starter character and test if it is blocked from 883 | the character at 'i' */ 884 | last_cc = unicode_get_cc(buf[i]); 885 | starter_pos = out_len - 1; 886 | while (starter_pos >= 0) { 887 | cc = unicode_get_cc(buf[starter_pos]); 888 | if (cc == 0) 889 | break; 890 | if (cc >= last_cc) 891 | goto next; 892 | last_cc = 256; 893 | starter_pos--; 894 | } 895 | if (starter_pos >= 0 && 896 | (p = compose_pair(buf[starter_pos], buf[i])) != 0) { 897 | buf[starter_pos] = p; 898 | i++; 899 | } else { 900 | next: 901 | buf[out_len++] = buf[i++]; 902 | } 903 | } 904 | *pdst = (uint32_t *)buf; 905 | return out_len; 906 | } 907 | 908 | /* char ranges for various unicode properties */ 909 | 910 | static int unicode_find_name(const char *name_table, const char *name) 911 | { 912 | const char *p, *r; 913 | int pos; 914 | size_t name_len, len; 915 | 916 | p = name_table; 917 | pos = 0; 918 | name_len = strlen(name); 919 | while (*p) { 920 | for(;;) { 921 | r = strchr(p, ','); 922 | if (!r) 923 | len = strlen(p); 924 | else 925 | len = r - p; 926 | if (len == name_len && !memcmp(p, name, name_len)) 927 | return pos; 928 | p += len + 1; 929 | if (!r) 930 | break; 931 | } 932 | pos++; 933 | } 934 | return -1; 935 | } 936 | 937 | /* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 938 | if not found */ 939 | int unicode_script(CharRange *cr, 940 | const char *script_name, BOOL is_ext) 941 | { 942 | int script_idx; 943 | const uint8_t *p, *p_end; 944 | uint32_t c, c1, b, n, v, v_len, i, type; 945 | CharRange cr1_s, *cr1; 946 | CharRange cr2_s, *cr2 = &cr2_s; 947 | BOOL is_common; 948 | 949 | script_idx = unicode_find_name(unicode_script_name_table, script_name); 950 | if (script_idx < 0) 951 | return -2; 952 | /* Note: we remove the "Unknown" Script */ 953 | script_idx += UNICODE_SCRIPT_Unknown + 1; 954 | 955 | is_common = (script_idx == UNICODE_SCRIPT_Common || 956 | script_idx == UNICODE_SCRIPT_Inherited); 957 | if (is_ext) { 958 | cr1 = &cr1_s; 959 | cr_init(cr1, cr->mem_opaque, cr->realloc_func); 960 | cr_init(cr2, cr->mem_opaque, cr->realloc_func); 961 | } else { 962 | cr1 = cr; 963 | } 964 | 965 | p = unicode_script_table; 966 | p_end = unicode_script_table + countof(unicode_script_table); 967 | c = 0; 968 | while (p < p_end) { 969 | b = *p++; 970 | type = b >> 7; 971 | n = b & 0x7f; 972 | if (n < 96) { 973 | } else if (n < 112) { 974 | n = (n - 96) << 8; 975 | n |= *p++; 976 | n += 96; 977 | } else { 978 | n = (n - 112) << 16; 979 | n |= *p++ << 8; 980 | n |= *p++; 981 | n += 96 + (1 << 12); 982 | } 983 | if (type == 0) 984 | v = 0; 985 | else 986 | v = *p++; 987 | c1 = c + n + 1; 988 | if (v == script_idx) { 989 | if (cr_add_interval(cr1, c, c1)) 990 | goto fail; 991 | } 992 | c = c1; 993 | } 994 | 995 | if (is_ext) { 996 | /* add the script extensions */ 997 | p = unicode_script_ext_table; 998 | p_end = unicode_script_ext_table + countof(unicode_script_ext_table); 999 | c = 0; 1000 | while (p < p_end) { 1001 | b = *p++; 1002 | if (b < 128) { 1003 | n = b; 1004 | } else if (b < 128 + 64) { 1005 | n = (b - 128) << 8; 1006 | n |= *p++; 1007 | n += 128; 1008 | } else { 1009 | n = (b - 128 - 64) << 16; 1010 | n |= *p++ << 8; 1011 | n |= *p++; 1012 | n += 128 + (1 << 14); 1013 | } 1014 | c1 = c + n + 1; 1015 | v_len = *p++; 1016 | if (is_common) { 1017 | if (v_len != 0) { 1018 | if (cr_add_interval(cr2, c, c1)) 1019 | goto fail; 1020 | } 1021 | } else { 1022 | for(i = 0; i < v_len; i++) { 1023 | if (p[i] == script_idx) { 1024 | if (cr_add_interval(cr2, c, c1)) 1025 | goto fail; 1026 | break; 1027 | } 1028 | } 1029 | } 1030 | p += v_len; 1031 | c = c1; 1032 | } 1033 | if (is_common) { 1034 | /* remove all the characters with script extensions */ 1035 | if (cr_invert(cr2)) 1036 | goto fail; 1037 | if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, 1038 | CR_OP_INTER)) 1039 | goto fail; 1040 | } else { 1041 | if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, 1042 | CR_OP_UNION)) 1043 | goto fail; 1044 | } 1045 | cr_free(cr1); 1046 | cr_free(cr2); 1047 | } 1048 | return 0; 1049 | fail: 1050 | if (is_ext) { 1051 | cr_free(cr1); 1052 | cr_free(cr2); 1053 | } 1054 | goto fail; 1055 | } 1056 | 1057 | #define M(id) (1U << UNICODE_GC_ ## id) 1058 | 1059 | static int unicode_general_category1(CharRange *cr, uint32_t gc_mask) 1060 | { 1061 | const uint8_t *p, *p_end; 1062 | uint32_t c, c0, b, n, v; 1063 | 1064 | p = unicode_gc_table; 1065 | p_end = unicode_gc_table + countof(unicode_gc_table); 1066 | c = 0; 1067 | while (p < p_end) { 1068 | b = *p++; 1069 | n = b >> 5; 1070 | v = b & 0x1f; 1071 | if (n == 7) { 1072 | n = *p++; 1073 | if (n < 128) { 1074 | n += 7; 1075 | } else if (n < 128 + 64) { 1076 | n = (n - 128) << 8; 1077 | n |= *p++; 1078 | n += 7 + 128; 1079 | } else { 1080 | n = (n - 128 - 64) << 16; 1081 | n |= *p++ << 8; 1082 | n |= *p++; 1083 | n += 7 + 128 + (1 << 14); 1084 | } 1085 | } 1086 | c0 = c; 1087 | c += n + 1; 1088 | if (v == 31) { 1089 | /* run of Lu / Ll */ 1090 | b = gc_mask & (M(Lu) | M(Ll)); 1091 | if (b != 0) { 1092 | if (b == (M(Lu) | M(Ll))) { 1093 | goto add_range; 1094 | } else { 1095 | c0 += ((gc_mask & M(Ll)) != 0); 1096 | for(; c0 < c; c0 += 2) { 1097 | if (cr_add_interval(cr, c0, c0 + 1)) 1098 | return -1; 1099 | } 1100 | } 1101 | } 1102 | } else if ((gc_mask >> v) & 1) { 1103 | add_range: 1104 | if (cr_add_interval(cr, c0, c)) 1105 | return -1; 1106 | } 1107 | } 1108 | return 0; 1109 | } 1110 | 1111 | static int unicode_prop1(CharRange *cr, int prop_idx) 1112 | { 1113 | const uint8_t *p, *p_end; 1114 | uint32_t c, c0, b, bit; 1115 | 1116 | p = unicode_prop_table[prop_idx]; 1117 | p_end = p + unicode_prop_len_table[prop_idx]; 1118 | c = 0; 1119 | bit = 0; 1120 | while (p < p_end) { 1121 | c0 = c; 1122 | b = *p++; 1123 | if (b < 64) { 1124 | c += (b >> 3) + 1; 1125 | if (bit) { 1126 | if (cr_add_interval(cr, c0, c)) 1127 | return -1; 1128 | } 1129 | bit ^= 1; 1130 | c0 = c; 1131 | c += (b & 7) + 1; 1132 | } else if (b >= 0x80) { 1133 | c += b - 0x80 + 1; 1134 | } else if (b < 0x60) { 1135 | c += (((b - 0x40) << 8) | p[0]) + 1; 1136 | p++; 1137 | } else { 1138 | c += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; 1139 | p += 2; 1140 | } 1141 | if (bit) { 1142 | if (cr_add_interval(cr, c0, c)) 1143 | return -1; 1144 | } 1145 | bit ^= 1; 1146 | } 1147 | return 0; 1148 | } 1149 | 1150 | #define CASE_U (1 << 0) 1151 | #define CASE_L (1 << 1) 1152 | #define CASE_F (1 << 2) 1153 | 1154 | /* use the case conversion table to generate range of characters. 1155 | CASE_U: set char if modified by uppercasing, 1156 | CASE_L: set char if modified by lowercasing, 1157 | CASE_F: set char if modified by case folding, 1158 | */ 1159 | static int unicode_case1(CharRange *cr, int case_mask) 1160 | { 1161 | #define MR(x) (1 << RUN_TYPE_ ## x) 1162 | const uint32_t tab_run_mask[3] = { 1163 | MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) | 1164 | MR(UF_D1_EXT) | MR(U_EXT) | MR(U_EXT2) | MR(U_EXT3), 1165 | 1166 | MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(L_EXT2), 1167 | 1168 | MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT), 1169 | }; 1170 | #undef MR 1171 | uint32_t mask, v, code, type, len, i, idx; 1172 | 1173 | if (case_mask == 0) 1174 | return 0; 1175 | mask = 0; 1176 | for(i = 0; i < 3; i++) { 1177 | if ((case_mask >> i) & 1) 1178 | mask |= tab_run_mask[i]; 1179 | } 1180 | for(idx = 0; idx < countof(case_conv_table1); idx++) { 1181 | v = case_conv_table1[idx]; 1182 | type = (v >> (32 - 17 - 7 - 4)) & 0xf; 1183 | code = v >> (32 - 17); 1184 | len = (v >> (32 - 17 - 7)) & 0x7f; 1185 | if ((mask >> type) & 1) { 1186 | // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1); 1187 | switch(type) { 1188 | case RUN_TYPE_UL: 1189 | if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) 1190 | goto def_case; 1191 | code += ((case_mask & CASE_U) != 0); 1192 | for(i = 0; i < len; i += 2) { 1193 | if (cr_add_interval(cr, code + i, code + i + 1)) 1194 | return -1; 1195 | } 1196 | break; 1197 | case RUN_TYPE_LSU: 1198 | if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) 1199 | goto def_case; 1200 | if (!(case_mask & CASE_U)) { 1201 | if (cr_add_interval(cr, code, code + 1)) 1202 | return -1; 1203 | } 1204 | if (cr_add_interval(cr, code + 1, code + 2)) 1205 | return -1; 1206 | if (case_mask & CASE_U) { 1207 | if (cr_add_interval(cr, code + 2, code + 3)) 1208 | return -1; 1209 | } 1210 | break; 1211 | default: 1212 | def_case: 1213 | if (cr_add_interval(cr, code, code + len)) 1214 | return -1; 1215 | break; 1216 | } 1217 | } 1218 | } 1219 | return 0; 1220 | } 1221 | 1222 | typedef enum { 1223 | POP_GC, 1224 | POP_PROP, 1225 | POP_CASE, 1226 | POP_UNION, 1227 | POP_INTER, 1228 | POP_XOR, 1229 | POP_INVERT, 1230 | POP_END, 1231 | } PropOPEnum; 1232 | 1233 | #define POP_STACK_LEN_MAX 4 1234 | 1235 | static int unicode_prop_ops(CharRange *cr, ...) 1236 | { 1237 | va_list ap; 1238 | CharRange stack[POP_STACK_LEN_MAX]; 1239 | int stack_len, op, ret, i; 1240 | uint32_t a; 1241 | 1242 | va_start(ap, cr); 1243 | stack_len = 0; 1244 | for(;;) { 1245 | op = va_arg(ap, int); 1246 | switch(op) { 1247 | case POP_GC: 1248 | assert(stack_len < POP_STACK_LEN_MAX); 1249 | a = va_arg(ap, int); 1250 | cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); 1251 | if (unicode_general_category1(&stack[stack_len - 1], a)) 1252 | goto fail; 1253 | break; 1254 | case POP_PROP: 1255 | assert(stack_len < POP_STACK_LEN_MAX); 1256 | a = va_arg(ap, int); 1257 | cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); 1258 | if (unicode_prop1(&stack[stack_len - 1], a)) 1259 | goto fail; 1260 | break; 1261 | case POP_CASE: 1262 | assert(stack_len < POP_STACK_LEN_MAX); 1263 | a = va_arg(ap, int); 1264 | cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); 1265 | if (unicode_case1(&stack[stack_len - 1], a)) 1266 | goto fail; 1267 | break; 1268 | case POP_UNION: 1269 | case POP_INTER: 1270 | case POP_XOR: 1271 | { 1272 | CharRange *cr1, *cr2, *cr3; 1273 | assert(stack_len >= 2); 1274 | assert(stack_len < POP_STACK_LEN_MAX); 1275 | cr1 = &stack[stack_len - 2]; 1276 | cr2 = &stack[stack_len - 1]; 1277 | cr3 = &stack[stack_len++]; 1278 | cr_init(cr3, cr->mem_opaque, cr->realloc_func); 1279 | if (cr_op(cr3, cr1->points, cr1->len, 1280 | cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION)) 1281 | goto fail; 1282 | cr_free(cr1); 1283 | cr_free(cr2); 1284 | *cr1 = *cr3; 1285 | stack_len -= 2; 1286 | } 1287 | break; 1288 | case POP_INVERT: 1289 | assert(stack_len >= 1); 1290 | if (cr_invert(&stack[stack_len - 1])) 1291 | goto fail; 1292 | break; 1293 | case POP_END: 1294 | goto done; 1295 | default: 1296 | abort(); 1297 | } 1298 | } 1299 | done: 1300 | assert(stack_len == 1); 1301 | ret = cr_copy(cr, &stack[0]); 1302 | cr_free(&stack[0]); 1303 | return ret; 1304 | fail: 1305 | for(i = 0; i < stack_len; i++) 1306 | cr_free(&stack[i]); 1307 | return -1; 1308 | } 1309 | 1310 | static const uint32_t unicode_gc_mask_table[] = { 1311 | M(Lu) | M(Ll) | M(Lt), /* LC */ 1312 | M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo), /* L */ 1313 | M(Mn) | M(Mc) | M(Me), /* M */ 1314 | M(Nd) | M(Nl) | M(No), /* N */ 1315 | M(Sm) | M(Sc) | M(Sk) | M(So), /* S */ 1316 | M(Pc) | M(Pd) | M(Ps) | M(Pe) | M(Pi) | M(Pf) | M(Po), /* P */ 1317 | M(Zs) | M(Zl) | M(Zp), /* Z */ 1318 | M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn), /* C */ 1319 | }; 1320 | 1321 | /* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 1322 | if not found */ 1323 | int unicode_general_category(CharRange *cr, const char *gc_name) 1324 | { 1325 | int gc_idx; 1326 | uint32_t gc_mask; 1327 | 1328 | gc_idx = unicode_find_name(unicode_gc_name_table, gc_name); 1329 | if (gc_idx < 0) 1330 | return -2; 1331 | if (gc_idx <= UNICODE_GC_Co) { 1332 | gc_mask = (uint64_t)1 << gc_idx; 1333 | } else { 1334 | gc_mask = unicode_gc_mask_table[gc_idx - UNICODE_GC_LC]; 1335 | } 1336 | return unicode_general_category1(cr, gc_mask); 1337 | } 1338 | 1339 | 1340 | /* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 1341 | if not found */ 1342 | int unicode_prop(CharRange *cr, const char *prop_name) 1343 | { 1344 | int prop_idx, ret; 1345 | 1346 | prop_idx = unicode_find_name(unicode_prop_name_table, prop_name); 1347 | if (prop_idx < 0) 1348 | return -2; 1349 | prop_idx += UNICODE_PROP_ASCII_Hex_Digit; 1350 | 1351 | ret = 0; 1352 | switch(prop_idx) { 1353 | case UNICODE_PROP_ASCII: 1354 | if (cr_add_interval(cr, 0x00, 0x7f + 1)) 1355 | return -1; 1356 | break; 1357 | case UNICODE_PROP_Any: 1358 | if (cr_add_interval(cr, 0x00000, 0x10ffff + 1)) 1359 | return -1; 1360 | break; 1361 | case UNICODE_PROP_Assigned: 1362 | ret = unicode_prop_ops(cr, 1363 | POP_GC, M(Cn), 1364 | POP_INVERT, 1365 | POP_END); 1366 | break; 1367 | case UNICODE_PROP_Math: 1368 | ret = unicode_prop_ops(cr, 1369 | POP_GC, M(Sm), 1370 | POP_PROP, UNICODE_PROP_Other_Math, 1371 | POP_UNION, 1372 | POP_END); 1373 | break; 1374 | case UNICODE_PROP_Lowercase: 1375 | ret = unicode_prop_ops(cr, 1376 | POP_GC, M(Ll), 1377 | POP_PROP, UNICODE_PROP_Other_Lowercase, 1378 | POP_UNION, 1379 | POP_END); 1380 | break; 1381 | case UNICODE_PROP_Uppercase: 1382 | ret = unicode_prop_ops(cr, 1383 | POP_GC, M(Lu), 1384 | POP_PROP, UNICODE_PROP_Other_Uppercase, 1385 | POP_UNION, 1386 | POP_END); 1387 | break; 1388 | case UNICODE_PROP_Cased: 1389 | ret = unicode_prop_ops(cr, 1390 | POP_GC, M(Lu) | M(Ll) | M(Lt), 1391 | POP_PROP, UNICODE_PROP_Other_Uppercase, 1392 | POP_UNION, 1393 | POP_PROP, UNICODE_PROP_Other_Lowercase, 1394 | POP_UNION, 1395 | POP_END); 1396 | break; 1397 | case UNICODE_PROP_Alphabetic: 1398 | ret = unicode_prop_ops(cr, 1399 | POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), 1400 | POP_PROP, UNICODE_PROP_Other_Uppercase, 1401 | POP_UNION, 1402 | POP_PROP, UNICODE_PROP_Other_Lowercase, 1403 | POP_UNION, 1404 | POP_PROP, UNICODE_PROP_Other_Alphabetic, 1405 | POP_UNION, 1406 | POP_END); 1407 | break; 1408 | case UNICODE_PROP_Grapheme_Base: 1409 | ret = unicode_prop_ops(cr, 1410 | POP_GC, M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn) | M(Zl) | M(Zp) | M(Me) | M(Mn), 1411 | POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, 1412 | POP_UNION, 1413 | POP_INVERT, 1414 | POP_END); 1415 | break; 1416 | case UNICODE_PROP_Grapheme_Extend: 1417 | ret = unicode_prop_ops(cr, 1418 | POP_GC, M(Me) | M(Mn), 1419 | POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, 1420 | POP_UNION, 1421 | POP_END); 1422 | break; 1423 | case UNICODE_PROP_XID_Start: 1424 | ret = unicode_prop_ops(cr, 1425 | POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), 1426 | POP_PROP, UNICODE_PROP_Other_ID_Start, 1427 | POP_UNION, 1428 | POP_PROP, UNICODE_PROP_Pattern_Syntax, 1429 | POP_PROP, UNICODE_PROP_Pattern_White_Space, 1430 | POP_UNION, 1431 | POP_PROP, UNICODE_PROP_XID_Start1, 1432 | POP_UNION, 1433 | POP_INVERT, 1434 | POP_INTER, 1435 | POP_END); 1436 | break; 1437 | case UNICODE_PROP_XID_Continue: 1438 | ret = unicode_prop_ops(cr, 1439 | POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | 1440 | M(Mn) | M(Mc) | M(Nd) | M(Pc), 1441 | POP_PROP, UNICODE_PROP_Other_ID_Start, 1442 | POP_UNION, 1443 | POP_PROP, UNICODE_PROP_Other_ID_Continue, 1444 | POP_UNION, 1445 | POP_PROP, UNICODE_PROP_Pattern_Syntax, 1446 | POP_PROP, UNICODE_PROP_Pattern_White_Space, 1447 | POP_UNION, 1448 | POP_PROP, UNICODE_PROP_XID_Continue1, 1449 | POP_UNION, 1450 | POP_INVERT, 1451 | POP_INTER, 1452 | POP_END); 1453 | break; 1454 | case UNICODE_PROP_Changes_When_Uppercased: 1455 | ret = unicode_case1(cr, CASE_U); 1456 | break; 1457 | case UNICODE_PROP_Changes_When_Lowercased: 1458 | ret = unicode_case1(cr, CASE_L); 1459 | break; 1460 | case UNICODE_PROP_Changes_When_Casemapped: 1461 | ret = unicode_case1(cr, CASE_U | CASE_L | CASE_F); 1462 | break; 1463 | case UNICODE_PROP_Changes_When_Titlecased: 1464 | ret = unicode_prop_ops(cr, 1465 | POP_CASE, CASE_U, 1466 | POP_PROP, UNICODE_PROP_Changes_When_Titlecased1, 1467 | POP_XOR, 1468 | POP_END); 1469 | break; 1470 | case UNICODE_PROP_Changes_When_Casefolded: 1471 | ret = unicode_prop_ops(cr, 1472 | POP_CASE, CASE_F, 1473 | POP_PROP, UNICODE_PROP_Changes_When_Casefolded1, 1474 | POP_XOR, 1475 | POP_END); 1476 | break; 1477 | case UNICODE_PROP_Changes_When_NFKC_Casefolded: 1478 | ret = unicode_prop_ops(cr, 1479 | POP_CASE, CASE_F, 1480 | POP_PROP, UNICODE_PROP_Changes_When_NFKC_Casefolded1, 1481 | POP_XOR, 1482 | POP_END); 1483 | break; 1484 | #if 0 1485 | case UNICODE_PROP_ID_Start: 1486 | ret = unicode_prop_ops(cr, 1487 | POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), 1488 | POP_PROP, UNICODE_PROP_Other_ID_Start, 1489 | POP_UNION, 1490 | POP_PROP, UNICODE_PROP_Pattern_Syntax, 1491 | POP_PROP, UNICODE_PROP_Pattern_White_Space, 1492 | POP_UNION, 1493 | POP_INVERT, 1494 | POP_INTER, 1495 | POP_END); 1496 | break; 1497 | case UNICODE_PROP_ID_Continue: 1498 | ret = unicode_prop_ops(cr, 1499 | POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | 1500 | M(Mn) | M(Mc) | M(Nd) | M(Pc), 1501 | POP_PROP, UNICODE_PROP_Other_ID_Start, 1502 | POP_UNION, 1503 | POP_PROP, UNICODE_PROP_Other_ID_Continue, 1504 | POP_UNION, 1505 | POP_PROP, UNICODE_PROP_Pattern_Syntax, 1506 | POP_PROP, UNICODE_PROP_Pattern_White_Space, 1507 | POP_UNION, 1508 | POP_INVERT, 1509 | POP_INTER, 1510 | POP_END); 1511 | break; 1512 | case UNICODE_PROP_Case_Ignorable: 1513 | ret = unicode_prop_ops(cr, 1514 | POP_GC, M(Mn) | M(Cf) | M(Lm) | M(Sk), 1515 | POP_PROP, UNICODE_PROP_Case_Ignorable1, 1516 | POP_XOR, 1517 | POP_END); 1518 | break; 1519 | #else 1520 | /* we use the existing tables */ 1521 | case UNICODE_PROP_ID_Continue: 1522 | ret = unicode_prop_ops(cr, 1523 | POP_PROP, UNICODE_PROP_ID_Start, 1524 | POP_PROP, UNICODE_PROP_ID_Continue1, 1525 | POP_XOR, 1526 | POP_END); 1527 | break; 1528 | #endif 1529 | default: 1530 | if (prop_idx >= countof(unicode_prop_table)) 1531 | return -2; 1532 | ret = unicode_prop1(cr, prop_idx); 1533 | break; 1534 | } 1535 | return ret; 1536 | } 1537 | 1538 | #endif /* CONFIG_ALL_UNICODE */ 1539 | -------------------------------------------------------------------------------- /libunicode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Unicode utilities 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef LIBUNICODE_H 25 | #define LIBUNICODE_H 26 | 27 | #include 28 | 29 | #define LRE_BOOL int /* for documentation purposes */ 30 | 31 | /* define it to include all the unicode tables (40KB larger) */ 32 | #define CONFIG_ALL_UNICODE 33 | 34 | #define LRE_CC_RES_LEN_MAX 3 35 | 36 | typedef enum { 37 | UNICODE_NFC, 38 | UNICODE_NFD, 39 | UNICODE_NFKC, 40 | UNICODE_NFKD, 41 | } UnicodeNormalizationEnum; 42 | 43 | int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); 44 | LRE_BOOL lre_is_cased(uint32_t c); 45 | LRE_BOOL lre_is_case_ignorable(uint32_t c); 46 | 47 | /* char ranges */ 48 | 49 | typedef struct { 50 | int len; /* in points, always even */ 51 | int size; 52 | uint32_t *points; /* points sorted by increasing value */ 53 | void *mem_opaque; 54 | void *(*realloc_func)(void *opaque, void *ptr, size_t size); 55 | } CharRange; 56 | 57 | typedef enum { 58 | CR_OP_UNION, 59 | CR_OP_INTER, 60 | CR_OP_XOR, 61 | } CharRangeOpEnum; 62 | 63 | void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); 64 | void cr_free(CharRange *cr); 65 | int cr_realloc(CharRange *cr, int size); 66 | int cr_copy(CharRange *cr, const CharRange *cr1); 67 | 68 | static inline int cr_add_point(CharRange *cr, uint32_t v) 69 | { 70 | if (cr->len >= cr->size) { 71 | if (cr_realloc(cr, cr->len + 1)) 72 | return -1; 73 | } 74 | cr->points[cr->len++] = v; 75 | return 0; 76 | } 77 | 78 | static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) 79 | { 80 | if ((cr->len + 2) > cr->size) { 81 | if (cr_realloc(cr, cr->len + 2)) 82 | return -1; 83 | } 84 | cr->points[cr->len++] = c1; 85 | cr->points[cr->len++] = c2; 86 | return 0; 87 | } 88 | 89 | int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); 90 | 91 | static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2) 92 | { 93 | uint32_t b_pt[2]; 94 | b_pt[0] = c1; 95 | b_pt[1] = c2 + 1; 96 | return cr_union1(cr, b_pt, 2); 97 | } 98 | 99 | int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, 100 | const uint32_t *b_pt, int b_len, int op); 101 | 102 | int cr_invert(CharRange *cr); 103 | 104 | #ifdef CONFIG_ALL_UNICODE 105 | 106 | LRE_BOOL lre_is_id_start(uint32_t c); 107 | LRE_BOOL lre_is_id_continue(uint32_t c); 108 | 109 | int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, 110 | UnicodeNormalizationEnum n_type, 111 | void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); 112 | 113 | /* Unicode character range functions */ 114 | 115 | int unicode_script(CharRange *cr, 116 | const char *script_name, LRE_BOOL is_ext); 117 | int unicode_general_category(CharRange *cr, const char *gc_name); 118 | int unicode_prop(CharRange *cr, const char *prop_name); 119 | 120 | #endif /* CONFIG_ALL_UNICODE */ 121 | 122 | #undef LRE_BOOL 123 | 124 | #endif /* LIBUNICODE_H */ 125 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Linux klist like system 3 | * 4 | * Copyright (c) 2016-2017 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef LIST_H 25 | #define LIST_H 26 | 27 | #ifndef NULL 28 | #include 29 | #endif 30 | 31 | struct list_head { 32 | struct list_head *prev; 33 | struct list_head *next; 34 | }; 35 | 36 | #define LIST_HEAD_INIT(el) { &(el), &(el) } 37 | 38 | /* return the pointer of type 'type *' containing 'el' as field 'member' */ 39 | #define list_entry(el, type, member) \ 40 | ((type *)((uint8_t *)(el) - offsetof(type, member))) 41 | 42 | static inline void init_list_head(struct list_head *head) 43 | { 44 | head->prev = head; 45 | head->next = head; 46 | } 47 | 48 | /* insert 'el' between 'prev' and 'next' */ 49 | static inline void __list_add(struct list_head *el, 50 | struct list_head *prev, struct list_head *next) 51 | { 52 | prev->next = el; 53 | el->prev = prev; 54 | el->next = next; 55 | next->prev = el; 56 | } 57 | 58 | /* add 'el' at the head of the list 'head' (= after element head) */ 59 | static inline void list_add(struct list_head *el, struct list_head *head) 60 | { 61 | __list_add(el, head, head->next); 62 | } 63 | 64 | /* add 'el' at the end of the list 'head' (= before element head) */ 65 | static inline void list_add_tail(struct list_head *el, struct list_head *head) 66 | { 67 | __list_add(el, head->prev, head); 68 | } 69 | 70 | static inline void list_del(struct list_head *el) 71 | { 72 | struct list_head *prev, *next; 73 | prev = el->prev; 74 | next = el->next; 75 | prev->next = next; 76 | next->prev = prev; 77 | el->prev = NULL; /* fail safe */ 78 | el->next = NULL; /* fail safe */ 79 | } 80 | 81 | static inline int list_empty(struct list_head *el) 82 | { 83 | return el->next == el; 84 | } 85 | 86 | #define list_for_each(el, head) \ 87 | for(el = (head)->next; el != (head); el = el->next) 88 | 89 | #define list_for_each_safe(el, el1, head) \ 90 | for(el = (head)->next, el1 = el->next; el != (head); \ 91 | el = el1, el1 = el->next) 92 | 93 | #define list_for_each_prev(el, head) \ 94 | for(el = (head)->prev; el != (head); el = el->prev) 95 | 96 | #define list_for_each_prev_safe(el, el1, head) \ 97 | for(el = (head)->prev, el1 = el->prev; el != (head); \ 98 | el = el1, el1 = el->prev) 99 | 100 | #endif /* LIST_H */ 101 | -------------------------------------------------------------------------------- /quickjs-atom.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS atom definitions 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * Copyright (c) 2017-2018 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #ifdef DEF 27 | 28 | /* Note: first atoms are considered as keywords in the parser */ 29 | DEF(null, "null") /* must be first */ 30 | DEF(false, "false") 31 | DEF(true, "true") 32 | DEF(if, "if") 33 | DEF(else, "else") 34 | DEF(return, "return") 35 | DEF(var, "var") 36 | DEF(this, "this") 37 | DEF(delete, "delete") 38 | DEF(void, "void") 39 | DEF(typeof, "typeof") 40 | DEF(new, "new") 41 | DEF(in, "in") 42 | DEF(instanceof, "instanceof") 43 | DEF(do, "do") 44 | DEF(while, "while") 45 | DEF(for, "for") 46 | DEF(break, "break") 47 | DEF(continue, "continue") 48 | DEF(switch, "switch") 49 | DEF(case, "case") 50 | DEF(default, "default") 51 | DEF(throw, "throw") 52 | DEF(try, "try") 53 | DEF(catch, "catch") 54 | DEF(finally, "finally") 55 | DEF(function, "function") 56 | DEF(debugger, "debugger") 57 | DEF(with, "with") 58 | /* FutureReservedWord */ 59 | DEF(class, "class") 60 | DEF(const, "const") 61 | DEF(enum, "enum") 62 | DEF(export, "export") 63 | DEF(extends, "extends") 64 | DEF(import, "import") 65 | DEF(super, "super") 66 | /* FutureReservedWords when parsing strict mode code */ 67 | DEF(implements, "implements") 68 | DEF(interface, "interface") 69 | DEF(let, "let") 70 | DEF(package, "package") 71 | DEF(private, "private") 72 | DEF(protected, "protected") 73 | DEF(public, "public") 74 | DEF(static, "static") 75 | DEF(yield, "yield") 76 | DEF(await, "await") 77 | 78 | /* empty string */ 79 | DEF(empty_string, "") 80 | /* identifiers */ 81 | DEF(length, "length") 82 | DEF(fileName, "fileName") 83 | DEF(lineNumber, "lineNumber") 84 | DEF(message, "message") 85 | DEF(errors, "errors") 86 | DEF(stack, "stack") 87 | DEF(name, "name") 88 | DEF(toString, "toString") 89 | DEF(toLocaleString, "toLocaleString") 90 | DEF(valueOf, "valueOf") 91 | DEF(eval, "eval") 92 | DEF(prototype, "prototype") 93 | DEF(constructor, "constructor") 94 | DEF(configurable, "configurable") 95 | DEF(writable, "writable") 96 | DEF(enumerable, "enumerable") 97 | DEF(value, "value") 98 | DEF(get, "get") 99 | DEF(set, "set") 100 | DEF(of, "of") 101 | DEF(__proto__, "__proto__") 102 | DEF(undefined, "undefined") 103 | DEF(number, "number") 104 | DEF(boolean, "boolean") 105 | DEF(string, "string") 106 | DEF(object, "object") 107 | DEF(symbol, "symbol") 108 | DEF(integer, "integer") 109 | DEF(unknown, "unknown") 110 | DEF(arguments, "arguments") 111 | DEF(callee, "callee") 112 | DEF(caller, "caller") 113 | DEF(_eval_, "") 114 | DEF(_ret_, "") 115 | DEF(_var_, "") 116 | DEF(_with_, "") 117 | DEF(lastIndex, "lastIndex") 118 | DEF(target, "target") 119 | DEF(index, "index") 120 | DEF(input, "input") 121 | DEF(defineProperties, "defineProperties") 122 | DEF(apply, "apply") 123 | DEF(join, "join") 124 | DEF(concat, "concat") 125 | DEF(split, "split") 126 | DEF(construct, "construct") 127 | DEF(getPrototypeOf, "getPrototypeOf") 128 | DEF(setPrototypeOf, "setPrototypeOf") 129 | DEF(isExtensible, "isExtensible") 130 | DEF(preventExtensions, "preventExtensions") 131 | DEF(has, "has") 132 | DEF(deleteProperty, "deleteProperty") 133 | DEF(defineProperty, "defineProperty") 134 | DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") 135 | DEF(ownKeys, "ownKeys") 136 | DEF(add, "add") 137 | DEF(done, "done") 138 | DEF(next, "next") 139 | DEF(values, "values") 140 | DEF(source, "source") 141 | DEF(flags, "flags") 142 | DEF(global, "global") 143 | DEF(unicode, "unicode") 144 | DEF(raw, "raw") 145 | DEF(new_target, "new.target") 146 | DEF(this_active_func, "this.active_func") 147 | DEF(home_object, "") 148 | DEF(computed_field, "") 149 | DEF(static_computed_field, "") /* must come after computed_fields */ 150 | DEF(class_fields_init, "") 151 | DEF(brand, "") 152 | DEF(hash_constructor, "#constructor") 153 | DEF(as, "as") 154 | DEF(from, "from") 155 | DEF(meta, "meta") 156 | DEF(_default_, "*default*") 157 | DEF(_star_, "*") 158 | DEF(Module, "Module") 159 | DEF(then, "then") 160 | DEF(resolve, "resolve") 161 | DEF(reject, "reject") 162 | DEF(promise, "promise") 163 | DEF(proxy, "proxy") 164 | DEF(revoke, "revoke") 165 | DEF(async, "async") 166 | DEF(exec, "exec") 167 | DEF(groups, "groups") 168 | DEF(status, "status") 169 | DEF(reason, "reason") 170 | DEF(globalThis, "globalThis") 171 | #ifdef CONFIG_BIGNUM 172 | DEF(bigint, "bigint") 173 | DEF(bigfloat, "bigfloat") 174 | DEF(bigdecimal, "bigdecimal") 175 | DEF(roundingMode, "roundingMode") 176 | DEF(maximumSignificantDigits, "maximumSignificantDigits") 177 | DEF(maximumFractionDigits, "maximumFractionDigits") 178 | #endif 179 | #ifdef CONFIG_ATOMICS 180 | DEF(not_equal, "not-equal") 181 | DEF(timed_out, "timed-out") 182 | DEF(ok, "ok") 183 | #endif 184 | DEF(toJSON, "toJSON") 185 | /* class names */ 186 | DEF(Object, "Object") 187 | DEF(Array, "Array") 188 | DEF(Error, "Error") 189 | DEF(Number, "Number") 190 | DEF(String, "String") 191 | DEF(Boolean, "Boolean") 192 | DEF(Symbol, "Symbol") 193 | DEF(Arguments, "Arguments") 194 | DEF(Math, "Math") 195 | DEF(JSON, "JSON") 196 | DEF(Date, "Date") 197 | DEF(Function, "Function") 198 | DEF(GeneratorFunction, "GeneratorFunction") 199 | DEF(ForInIterator, "ForInIterator") 200 | DEF(RegExp, "RegExp") 201 | DEF(ArrayBuffer, "ArrayBuffer") 202 | DEF(SharedArrayBuffer, "SharedArrayBuffer") 203 | /* must keep same order as class IDs for typed arrays */ 204 | DEF(Uint8ClampedArray, "Uint8ClampedArray") 205 | DEF(Int8Array, "Int8Array") 206 | DEF(Uint8Array, "Uint8Array") 207 | DEF(Int16Array, "Int16Array") 208 | DEF(Uint16Array, "Uint16Array") 209 | DEF(Int32Array, "Int32Array") 210 | DEF(Uint32Array, "Uint32Array") 211 | #ifdef CONFIG_BIGNUM 212 | DEF(BigInt64Array, "BigInt64Array") 213 | DEF(BigUint64Array, "BigUint64Array") 214 | #endif 215 | DEF(Float32Array, "Float32Array") 216 | DEF(Float64Array, "Float64Array") 217 | DEF(DataView, "DataView") 218 | #ifdef CONFIG_BIGNUM 219 | DEF(BigInt, "BigInt") 220 | DEF(BigFloat, "BigFloat") 221 | DEF(BigFloatEnv, "BigFloatEnv") 222 | DEF(BigDecimal, "BigDecimal") 223 | DEF(OperatorSet, "OperatorSet") 224 | DEF(Operators, "Operators") 225 | #endif 226 | DEF(Map, "Map") 227 | DEF(Set, "Set") /* Map + 1 */ 228 | DEF(WeakMap, "WeakMap") /* Map + 2 */ 229 | DEF(WeakSet, "WeakSet") /* Map + 3 */ 230 | DEF(Map_Iterator, "Map Iterator") 231 | DEF(Set_Iterator, "Set Iterator") 232 | DEF(Array_Iterator, "Array Iterator") 233 | DEF(String_Iterator, "String Iterator") 234 | DEF(RegExp_String_Iterator, "RegExp String Iterator") 235 | DEF(Generator, "Generator") 236 | DEF(Proxy, "Proxy") 237 | DEF(Promise, "Promise") 238 | DEF(PromiseResolveFunction, "PromiseResolveFunction") 239 | DEF(PromiseRejectFunction, "PromiseRejectFunction") 240 | DEF(AsyncFunction, "AsyncFunction") 241 | DEF(AsyncFunctionResolve, "AsyncFunctionResolve") 242 | DEF(AsyncFunctionReject, "AsyncFunctionReject") 243 | DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") 244 | DEF(AsyncGenerator, "AsyncGenerator") 245 | DEF(EvalError, "EvalError") 246 | DEF(RangeError, "RangeError") 247 | DEF(ReferenceError, "ReferenceError") 248 | DEF(SyntaxError, "SyntaxError") 249 | DEF(TypeError, "TypeError") 250 | DEF(URIError, "URIError") 251 | DEF(InternalError, "InternalError") 252 | /* private symbols */ 253 | DEF(Private_brand, "") 254 | /* symbols */ 255 | DEF(Symbol_toPrimitive, "Symbol.toPrimitive") 256 | DEF(Symbol_iterator, "Symbol.iterator") 257 | DEF(Symbol_match, "Symbol.match") 258 | DEF(Symbol_matchAll, "Symbol.matchAll") 259 | DEF(Symbol_replace, "Symbol.replace") 260 | DEF(Symbol_search, "Symbol.search") 261 | DEF(Symbol_split, "Symbol.split") 262 | DEF(Symbol_toStringTag, "Symbol.toStringTag") 263 | DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") 264 | DEF(Symbol_hasInstance, "Symbol.hasInstance") 265 | DEF(Symbol_species, "Symbol.species") 266 | DEF(Symbol_unscopables, "Symbol.unscopables") 267 | DEF(Symbol_asyncIterator, "Symbol.asyncIterator") 268 | #ifdef CONFIG_BIGNUM 269 | DEF(Symbol_operatorSet, "Symbol.operatorSet") 270 | #endif 271 | 272 | #endif /* DEF */ 273 | -------------------------------------------------------------------------------- /quickjs-opcode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS opcode definitions 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * Copyright (c) 2017-2018 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #ifdef FMT 27 | FMT(none) 28 | FMT(none_int) 29 | FMT(none_loc) 30 | FMT(none_arg) 31 | FMT(none_var_ref) 32 | FMT(u8) 33 | FMT(i8) 34 | FMT(loc8) 35 | FMT(const8) 36 | FMT(label8) 37 | FMT(u16) 38 | FMT(i16) 39 | FMT(label16) 40 | FMT(npop) 41 | FMT(npopx) 42 | FMT(npop_u16) 43 | FMT(loc) 44 | FMT(arg) 45 | FMT(var_ref) 46 | FMT(u32) 47 | FMT(i32) 48 | FMT(const) 49 | FMT(label) 50 | FMT(atom) 51 | FMT(atom_u8) 52 | FMT(atom_u16) 53 | FMT(atom_label_u8) 54 | FMT(atom_label_u16) 55 | FMT(label_u16) 56 | #undef FMT 57 | #endif /* FMT */ 58 | 59 | #ifdef DEF 60 | 61 | #ifndef def 62 | #define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) 63 | #endif 64 | 65 | DEF(invalid, 1, 0, 0, none) /* never emitted */ 66 | 67 | /* push values */ 68 | DEF( push_i32, 5, 0, 1, i32) 69 | DEF( push_const, 5, 0, 1, const) 70 | DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ 71 | DEF(push_atom_value, 5, 0, 1, atom) 72 | DEF( private_symbol, 5, 0, 1, atom) 73 | DEF( undefined, 1, 0, 1, none) 74 | DEF( null, 1, 0, 1, none) 75 | DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ 76 | DEF( push_false, 1, 0, 1, none) 77 | DEF( push_true, 1, 0, 1, none) 78 | DEF( object, 1, 0, 1, none) 79 | DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ 80 | DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ 81 | 82 | DEF( drop, 1, 1, 0, none) /* a -> */ 83 | DEF( nip, 1, 2, 1, none) /* a b -> b */ 84 | DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ 85 | DEF( dup, 1, 1, 2, none) /* a -> a a */ 86 | DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ 87 | DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ 88 | DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ 89 | DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ 90 | DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ 91 | DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ 92 | DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ 93 | DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ 94 | DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ 95 | DEF( swap, 1, 2, 2, none) /* a b -> b a */ 96 | DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ 97 | DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ 98 | DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ 99 | DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ 100 | DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ 101 | 102 | DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ 103 | DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ 104 | DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ 105 | DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ 106 | DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ 107 | DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ 108 | DEF( apply, 3, 3, 1, u16) 109 | DEF( return, 1, 1, 0, none) 110 | DEF( return_undef, 1, 0, 0, none) 111 | DEF(check_ctor_return, 1, 1, 2, none) 112 | DEF( check_ctor, 1, 0, 0, none) 113 | DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ 114 | DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ 115 | DEF( return_async, 1, 1, 0, none) 116 | DEF( throw, 1, 1, 0, none) 117 | DEF( throw_var, 6, 0, 0, atom_u8) 118 | DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ 119 | DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ 120 | DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a 121 | bytecode string */ 122 | DEF( get_super, 1, 1, 1, none) 123 | DEF( import, 1, 1, 1, none) /* dynamic module import */ 124 | 125 | DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */ 126 | DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ 127 | DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ 128 | DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ 129 | DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ 130 | DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */ 131 | 132 | DEF( get_ref_value, 1, 2, 3, none) 133 | DEF( put_ref_value, 1, 3, 0, none) 134 | 135 | DEF( define_var, 6, 0, 0, atom_u8) 136 | DEF(check_define_var, 6, 0, 0, atom_u8) 137 | DEF( define_func, 6, 1, 0, atom_u8) 138 | DEF( get_field, 5, 1, 1, atom) 139 | DEF( get_field2, 5, 1, 2, atom) 140 | DEF( put_field, 5, 2, 0, atom) 141 | DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ 142 | DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ 143 | DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ 144 | DEF( get_array_el, 1, 2, 1, none) 145 | DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ 146 | DEF( put_array_el, 1, 3, 0, none) 147 | DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ 148 | DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ 149 | DEF( define_field, 5, 2, 1, atom) 150 | DEF( set_name, 5, 1, 1, atom) 151 | DEF(set_name_computed, 1, 2, 2, none) 152 | DEF( set_proto, 1, 2, 1, none) 153 | DEF(set_home_object, 1, 2, 2, none) 154 | DEF(define_array_el, 1, 3, 2, none) 155 | DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ 156 | DEF(copy_data_properties, 2, 3, 3, u8) 157 | DEF( define_method, 6, 2, 1, atom_u8) 158 | DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ 159 | DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ 160 | DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ 161 | 162 | DEF( get_loc, 3, 0, 1, loc) 163 | DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ 164 | DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ 165 | DEF( get_arg, 3, 0, 1, arg) 166 | DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ 167 | DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ 168 | DEF( get_var_ref, 3, 0, 1, var_ref) 169 | DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ 170 | DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ 171 | DEF(set_loc_uninitialized, 3, 0, 0, loc) 172 | DEF( get_loc_check, 3, 0, 1, loc) 173 | DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ 174 | DEF( put_loc_check_init, 3, 1, 0, loc) 175 | DEF(get_var_ref_check, 3, 0, 1, var_ref) 176 | DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ 177 | DEF(put_var_ref_check_init, 3, 1, 0, var_ref) 178 | DEF( close_loc, 3, 0, 0, loc) 179 | DEF( if_false, 5, 1, 0, label) 180 | DEF( if_true, 5, 1, 0, label) /* must come after if_false */ 181 | DEF( goto, 5, 0, 0, label) /* must come after if_true */ 182 | DEF( catch, 5, 0, 1, label) 183 | DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ 184 | DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ 185 | 186 | DEF( to_object, 1, 1, 1, none) 187 | //DEF( to_string, 1, 1, 1, none) 188 | DEF( to_propkey, 1, 1, 1, none) 189 | DEF( to_propkey2, 1, 2, 2, none) 190 | 191 | DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ 192 | DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ 193 | DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ 194 | DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ 195 | DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ 196 | DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) 197 | 198 | DEF( make_loc_ref, 7, 0, 2, atom_u16) 199 | DEF( make_arg_ref, 7, 0, 2, atom_u16) 200 | DEF(make_var_ref_ref, 7, 0, 2, atom_u16) 201 | DEF( make_var_ref, 5, 0, 2, atom) 202 | 203 | DEF( for_in_start, 1, 1, 1, none) 204 | DEF( for_of_start, 1, 1, 3, none) 205 | DEF(for_await_of_start, 1, 1, 3, none) 206 | DEF( for_in_next, 1, 1, 3, none) 207 | DEF( for_of_next, 2, 3, 5, u8) 208 | DEF(for_await_of_next, 1, 3, 4, none) 209 | DEF(iterator_get_value_done, 1, 1, 2, none) 210 | DEF( iterator_close, 1, 3, 0, none) 211 | DEF(iterator_close_return, 1, 4, 4, none) 212 | DEF(async_iterator_close, 1, 3, 2, none) 213 | DEF(async_iterator_next, 1, 4, 4, none) 214 | DEF(async_iterator_get, 2, 4, 5, u8) 215 | DEF( initial_yield, 1, 0, 0, none) 216 | DEF( yield, 1, 1, 2, none) 217 | DEF( yield_star, 1, 2, 2, none) 218 | DEF(async_yield_star, 1, 1, 2, none) 219 | DEF( await, 1, 1, 1, none) 220 | 221 | /* arithmetic/logic operations */ 222 | DEF( neg, 1, 1, 1, none) 223 | DEF( plus, 1, 1, 1, none) 224 | DEF( dec, 1, 1, 1, none) 225 | DEF( inc, 1, 1, 1, none) 226 | DEF( post_dec, 1, 1, 2, none) 227 | DEF( post_inc, 1, 1, 2, none) 228 | DEF( dec_loc, 2, 0, 0, loc8) 229 | DEF( inc_loc, 2, 0, 0, loc8) 230 | DEF( add_loc, 2, 1, 0, loc8) 231 | DEF( not, 1, 1, 1, none) 232 | DEF( lnot, 1, 1, 1, none) 233 | DEF( typeof, 1, 1, 1, none) 234 | DEF( delete, 1, 2, 1, none) 235 | DEF( delete_var, 5, 0, 1, atom) 236 | 237 | DEF( mul, 1, 2, 1, none) 238 | DEF( div, 1, 2, 1, none) 239 | DEF( mod, 1, 2, 1, none) 240 | DEF( add, 1, 2, 1, none) 241 | DEF( sub, 1, 2, 1, none) 242 | DEF( pow, 1, 2, 1, none) 243 | DEF( shl, 1, 2, 1, none) 244 | DEF( sar, 1, 2, 1, none) 245 | DEF( shr, 1, 2, 1, none) 246 | DEF( lt, 1, 2, 1, none) 247 | DEF( lte, 1, 2, 1, none) 248 | DEF( gt, 1, 2, 1, none) 249 | DEF( gte, 1, 2, 1, none) 250 | DEF( instanceof, 1, 2, 1, none) 251 | DEF( in, 1, 2, 1, none) 252 | DEF( eq, 1, 2, 1, none) 253 | DEF( neq, 1, 2, 1, none) 254 | DEF( strict_eq, 1, 2, 1, none) 255 | DEF( strict_neq, 1, 2, 1, none) 256 | DEF( and, 1, 2, 1, none) 257 | DEF( xor, 1, 2, 1, none) 258 | DEF( or, 1, 2, 1, none) 259 | DEF(is_undefined_or_null, 1, 1, 1, none) 260 | #ifdef CONFIG_BIGNUM 261 | DEF( mul_pow10, 1, 2, 1, none) 262 | DEF( math_mod, 1, 2, 1, none) 263 | #endif 264 | /* must be the last non short and non temporary opcode */ 265 | DEF( nop, 1, 0, 0, none) 266 | 267 | /* temporary opcodes: never emitted in the final bytecode */ 268 | 269 | def(set_arg_valid_upto, 3, 0, 0, arg) /* emitted in phase 1, removed in phase 2 */ 270 | 271 | def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ 272 | def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ 273 | 274 | def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ 275 | 276 | def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ 277 | def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ 278 | def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ 279 | def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ 280 | def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ 281 | def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ 282 | def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ 283 | def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ 284 | def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ 285 | def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ 286 | 287 | def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ 288 | 289 | def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */ 290 | 291 | #if SHORT_OPCODES 292 | DEF( push_minus1, 1, 0, 1, none_int) 293 | DEF( push_0, 1, 0, 1, none_int) 294 | DEF( push_1, 1, 0, 1, none_int) 295 | DEF( push_2, 1, 0, 1, none_int) 296 | DEF( push_3, 1, 0, 1, none_int) 297 | DEF( push_4, 1, 0, 1, none_int) 298 | DEF( push_5, 1, 0, 1, none_int) 299 | DEF( push_6, 1, 0, 1, none_int) 300 | DEF( push_7, 1, 0, 1, none_int) 301 | DEF( push_i8, 2, 0, 1, i8) 302 | DEF( push_i16, 3, 0, 1, i16) 303 | DEF( push_const8, 2, 0, 1, const8) 304 | DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ 305 | DEF(push_empty_string, 1, 0, 1, none) 306 | 307 | DEF( get_loc8, 2, 0, 1, loc8) 308 | DEF( put_loc8, 2, 1, 0, loc8) 309 | DEF( set_loc8, 2, 1, 1, loc8) 310 | 311 | DEF( get_loc0, 1, 0, 1, none_loc) 312 | DEF( get_loc1, 1, 0, 1, none_loc) 313 | DEF( get_loc2, 1, 0, 1, none_loc) 314 | DEF( get_loc3, 1, 0, 1, none_loc) 315 | DEF( put_loc0, 1, 1, 0, none_loc) 316 | DEF( put_loc1, 1, 1, 0, none_loc) 317 | DEF( put_loc2, 1, 1, 0, none_loc) 318 | DEF( put_loc3, 1, 1, 0, none_loc) 319 | DEF( set_loc0, 1, 1, 1, none_loc) 320 | DEF( set_loc1, 1, 1, 1, none_loc) 321 | DEF( set_loc2, 1, 1, 1, none_loc) 322 | DEF( set_loc3, 1, 1, 1, none_loc) 323 | DEF( get_arg0, 1, 0, 1, none_arg) 324 | DEF( get_arg1, 1, 0, 1, none_arg) 325 | DEF( get_arg2, 1, 0, 1, none_arg) 326 | DEF( get_arg3, 1, 0, 1, none_arg) 327 | DEF( put_arg0, 1, 1, 0, none_arg) 328 | DEF( put_arg1, 1, 1, 0, none_arg) 329 | DEF( put_arg2, 1, 1, 0, none_arg) 330 | DEF( put_arg3, 1, 1, 0, none_arg) 331 | DEF( set_arg0, 1, 1, 1, none_arg) 332 | DEF( set_arg1, 1, 1, 1, none_arg) 333 | DEF( set_arg2, 1, 1, 1, none_arg) 334 | DEF( set_arg3, 1, 1, 1, none_arg) 335 | DEF( get_var_ref0, 1, 0, 1, none_var_ref) 336 | DEF( get_var_ref1, 1, 0, 1, none_var_ref) 337 | DEF( get_var_ref2, 1, 0, 1, none_var_ref) 338 | DEF( get_var_ref3, 1, 0, 1, none_var_ref) 339 | DEF( put_var_ref0, 1, 1, 0, none_var_ref) 340 | DEF( put_var_ref1, 1, 1, 0, none_var_ref) 341 | DEF( put_var_ref2, 1, 1, 0, none_var_ref) 342 | DEF( put_var_ref3, 1, 1, 0, none_var_ref) 343 | DEF( set_var_ref0, 1, 1, 1, none_var_ref) 344 | DEF( set_var_ref1, 1, 1, 1, none_var_ref) 345 | DEF( set_var_ref2, 1, 1, 1, none_var_ref) 346 | DEF( set_var_ref3, 1, 1, 1, none_var_ref) 347 | 348 | DEF( get_length, 1, 1, 1, none) 349 | 350 | DEF( if_false8, 2, 1, 0, label8) 351 | DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ 352 | DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ 353 | DEF( goto16, 3, 0, 0, label16) 354 | 355 | DEF( call0, 1, 1, 1, npopx) 356 | DEF( call1, 1, 1, 1, npopx) 357 | DEF( call2, 1, 1, 1, npopx) 358 | DEF( call3, 1, 1, 1, npopx) 359 | 360 | DEF( is_undefined, 1, 1, 1, none) 361 | DEF( is_null, 1, 1, 1, none) 362 | DEF( is_function, 1, 1, 1, none) 363 | #endif 364 | 365 | #undef DEF 366 | #undef def 367 | #endif /* DEF */ 368 | -------------------------------------------------------------------------------- /quickjs.go: -------------------------------------------------------------------------------- 1 | package quickjs 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "math/big" 8 | "sync" 9 | "sync/atomic" 10 | "unsafe" 11 | ) 12 | 13 | /* 14 | #cgo CFLAGS: -D_GNU_SOURCE 15 | #cgo CFLAGS: -DCONFIG_BIGNUM 16 | #cgo CFLAGS: -fno-asynchronous-unwind-tables 17 | #cgo LDFLAGS: -lm -lpthread 18 | 19 | #include "bridge.h" 20 | */ 21 | import "C" 22 | 23 | type Runtime struct { 24 | ref *C.JSRuntime 25 | } 26 | 27 | func NewRuntime() Runtime { 28 | rt := Runtime{ref: C.JS_NewRuntime()} 29 | C.JS_SetCanBlock(rt.ref, C.int(1)) 30 | return rt 31 | } 32 | 33 | func (r Runtime) RunGC() { C.JS_RunGC(r.ref) } 34 | 35 | func (r Runtime) Free() { C.JS_FreeRuntime(r.ref) } 36 | 37 | func (r Runtime) NewContext() *Context { 38 | ref := C.JS_NewContext(r.ref) 39 | 40 | C.JS_AddIntrinsicBigFloat(ref) 41 | C.JS_AddIntrinsicBigDecimal(ref) 42 | C.JS_AddIntrinsicOperators(ref) 43 | C.JS_EnableBignumExt(ref, C.int(1)) 44 | 45 | return &Context{ref: ref} 46 | } 47 | 48 | func (r Runtime) ExecutePendingJob() (Context, error) { 49 | var ctx Context 50 | 51 | err := C.JS_ExecutePendingJob(r.ref, &ctx.ref) 52 | if err <= 0 { 53 | if err == 0 { 54 | return ctx, io.EOF 55 | } 56 | return ctx, ctx.Exception() 57 | } 58 | 59 | return ctx, nil 60 | } 61 | 62 | type Function func(ctx *Context, this Value, args []Value) Value 63 | 64 | type funcEntry struct { 65 | ctx *Context 66 | fn Function 67 | } 68 | 69 | var funcPtrLen int64 70 | var funcPtrLock sync.Mutex 71 | var funcPtrStore = make(map[int64]funcEntry) 72 | var funcPtrClassID C.JSClassID 73 | 74 | func init() { C.JS_NewClassID(&funcPtrClassID) } 75 | 76 | func storeFuncPtr(v funcEntry) int64 { 77 | id := atomic.AddInt64(&funcPtrLen, 1) - 1 78 | funcPtrLock.Lock() 79 | defer funcPtrLock.Unlock() 80 | funcPtrStore[id] = v 81 | return id 82 | } 83 | 84 | func restoreFuncPtr(ptr int64) funcEntry { 85 | funcPtrLock.Lock() 86 | defer funcPtrLock.Unlock() 87 | return funcPtrStore[ptr] 88 | } 89 | 90 | //func freeFuncPtr(ptr int64) { 91 | // funcPtrLock.Lock() 92 | // defer funcPtrLock.Unlock() 93 | // delete(funcPtrStore, ptr) 94 | //} 95 | 96 | //export proxy 97 | func proxy(ctx *C.JSContext, thisVal C.JSValueConst, argc C.int, argv *C.JSValueConst) C.JSValue { 98 | // The maximum capacity of the following two slices is limited to (2^29)-1 to remain compatible 99 | // with 32-bit platforms. The size of a `*C.char` (a pointer) is 4 Byte on a 32-bit system 100 | // and (2^29)*4 == math.MaxInt32 + 1. -- See issue golang/go#13656 101 | refs := (*[(1 << 29) - 1]C.JSValueConst)(unsafe.Pointer(argv))[:argc:argc] 102 | 103 | id := C.int64_t(0) 104 | C.JS_ToInt64(ctx, &id, refs[0]) 105 | 106 | entry := restoreFuncPtr(int64(id)) 107 | 108 | args := make([]Value, len(refs)-1) 109 | for i := 0; i < len(args); i++ { 110 | args[i].ctx = entry.ctx 111 | args[i].ref = refs[1+i] 112 | } 113 | 114 | result := entry.fn(entry.ctx, Value{ctx: entry.ctx, ref: thisVal}, args) 115 | 116 | return result.ref 117 | } 118 | 119 | type Context struct { 120 | ref *C.JSContext 121 | globals *Value 122 | proxy *Value 123 | } 124 | 125 | func (ctx *Context) Free() { 126 | if ctx.proxy != nil { 127 | ctx.proxy.Free() 128 | } 129 | if ctx.globals != nil { 130 | ctx.globals.Free() 131 | } 132 | 133 | C.JS_FreeContext(ctx.ref) 134 | } 135 | 136 | func (ctx *Context) Function(fn Function) Value { 137 | val := ctx.eval(`(proxy, id) => function() { return proxy.call(this, id, ...arguments); }`) 138 | if val.IsException() { 139 | return val 140 | } 141 | defer val.Free() 142 | 143 | funcPtr := storeFuncPtr(funcEntry{ctx: ctx, fn: fn}) 144 | funcPtrVal := ctx.Int64(funcPtr) 145 | 146 | if ctx.proxy == nil { 147 | ctx.proxy = &Value{ 148 | ctx: ctx, 149 | ref: C.JS_NewCFunction(ctx.ref, (*C.JSCFunction)(unsafe.Pointer(C.InvokeProxy)), nil, C.int(0)), 150 | } 151 | } 152 | 153 | args := []C.JSValue{ctx.proxy.ref, funcPtrVal.ref} 154 | 155 | return Value{ctx: ctx, ref: C.JS_Call(ctx.ref, val.ref, ctx.Null().ref, C.int(len(args)), &args[0])} 156 | } 157 | 158 | func (ctx *Context) Null() Value { 159 | return Value{ctx: ctx, ref: C.JS_NewNull()} 160 | } 161 | 162 | func (ctx *Context) Undefined() Value { 163 | return Value{ctx: ctx, ref: C.JS_NewUndefined()} 164 | } 165 | 166 | func (ctx *Context) Uninitialized() Value { 167 | return Value{ctx: ctx, ref: C.JS_NewUninitialized()} 168 | } 169 | 170 | func (ctx *Context) Error(err error) Value { 171 | val := Value{ctx: ctx, ref: C.JS_NewError(ctx.ref)} 172 | val.Set("message", ctx.String(err.Error())) 173 | return val 174 | } 175 | 176 | func (ctx *Context) Bool(b bool) Value { 177 | bv := 0 178 | if b { 179 | bv = 1 180 | } 181 | return Value{ctx: ctx, ref: C.JS_NewBool(ctx.ref, C.int(bv))} 182 | } 183 | 184 | func (ctx *Context) Int32(v int32) Value { 185 | return Value{ctx: ctx, ref: C.JS_NewInt32(ctx.ref, C.int32_t(v))} 186 | } 187 | 188 | func (ctx *Context) Int64(v int64) Value { 189 | return Value{ctx: ctx, ref: C.JS_NewInt64(ctx.ref, C.int64_t(v))} 190 | } 191 | 192 | func (ctx *Context) Uint32(v uint32) Value { 193 | return Value{ctx: ctx, ref: C.JS_NewUint32(ctx.ref, C.uint32_t(v))} 194 | } 195 | 196 | func (ctx *Context) BigUint64(v uint64) Value { 197 | return Value{ctx: ctx, ref: C.JS_NewBigUint64(ctx.ref, C.uint64_t(v))} 198 | } 199 | 200 | func (ctx *Context) Float64(v float64) Value { 201 | return Value{ctx: ctx, ref: C.JS_NewFloat64(ctx.ref, C.double(v))} 202 | } 203 | 204 | func (ctx *Context) String(v string) Value { 205 | ptr := C.CString(v) 206 | defer C.free(unsafe.Pointer(ptr)) 207 | return Value{ctx: ctx, ref: C.JS_NewString(ctx.ref, ptr)} 208 | } 209 | 210 | func (ctx *Context) Atom(v string) Atom { 211 | ptr := C.CString(v) 212 | defer C.free(unsafe.Pointer(ptr)) 213 | return Atom{ctx: ctx, ref: C.JS_NewAtom(ctx.ref, ptr)} 214 | } 215 | 216 | func (ctx *Context) eval(code string) Value { return ctx.evalFile(code, "code") } 217 | 218 | func (ctx *Context) evalFile(code, filename string) Value { 219 | codePtr := C.CString(code) 220 | defer C.free(unsafe.Pointer(codePtr)) 221 | 222 | filenamePtr := C.CString(filename) 223 | defer C.free(unsafe.Pointer(filenamePtr)) 224 | 225 | return Value{ctx: ctx, ref: C.JS_Eval(ctx.ref, codePtr, C.size_t(len(code)), filenamePtr, C.int(0))} 226 | } 227 | 228 | func (ctx *Context) Eval(code string) (Value, error) { return ctx.EvalFile(code, "code") } 229 | 230 | func (ctx *Context) EvalFile(code, filename string) (Value, error) { 231 | val := ctx.evalFile(code, filename) 232 | if val.IsException() { 233 | return val, ctx.Exception() 234 | } 235 | return val, nil 236 | } 237 | 238 | func (ctx *Context) Globals() Value { 239 | if ctx.globals == nil { 240 | ctx.globals = &Value{ 241 | ctx: ctx, 242 | ref: C.JS_GetGlobalObject(ctx.ref), 243 | } 244 | } 245 | return *ctx.globals 246 | } 247 | 248 | func (ctx *Context) Throw(v Value) Value { 249 | return Value{ctx: ctx, ref: C.JS_Throw(ctx.ref, v.ref)} 250 | } 251 | 252 | func (ctx *Context) ThrowError(err error) Value { return ctx.Throw(ctx.Error(err)) } 253 | 254 | func (ctx *Context) ThrowSyntaxError(format string, args ...interface{}) Value { 255 | cause := fmt.Sprintf(format, args...) 256 | causePtr := C.CString(cause) 257 | defer C.free(unsafe.Pointer(causePtr)) 258 | return Value{ctx: ctx, ref: C.ThrowSyntaxError(ctx.ref, causePtr)} 259 | } 260 | 261 | func (ctx *Context) ThrowTypeError(format string, args ...interface{}) Value { 262 | cause := fmt.Sprintf(format, args...) 263 | causePtr := C.CString(cause) 264 | defer C.free(unsafe.Pointer(causePtr)) 265 | return Value{ctx: ctx, ref: C.ThrowTypeError(ctx.ref, causePtr)} 266 | } 267 | 268 | func (ctx *Context) ThrowReferenceError(format string, args ...interface{}) Value { 269 | cause := fmt.Sprintf(format, args...) 270 | causePtr := C.CString(cause) 271 | defer C.free(unsafe.Pointer(causePtr)) 272 | return Value{ctx: ctx, ref: C.ThrowReferenceError(ctx.ref, causePtr)} 273 | } 274 | 275 | func (ctx *Context) ThrowRangeError(format string, args ...interface{}) Value { 276 | cause := fmt.Sprintf(format, args...) 277 | causePtr := C.CString(cause) 278 | defer C.free(unsafe.Pointer(causePtr)) 279 | return Value{ctx: ctx, ref: C.ThrowRangeError(ctx.ref, causePtr)} 280 | } 281 | 282 | func (ctx *Context) ThrowInternalError(format string, args ...interface{}) Value { 283 | cause := fmt.Sprintf(format, args...) 284 | causePtr := C.CString(cause) 285 | defer C.free(unsafe.Pointer(causePtr)) 286 | return Value{ctx: ctx, ref: C.ThrowInternalError(ctx.ref, causePtr)} 287 | } 288 | 289 | func (ctx *Context) Exception() error { 290 | val := Value{ctx: ctx, ref: C.JS_GetException(ctx.ref)} 291 | defer val.Free() 292 | return val.Error() 293 | } 294 | 295 | func (ctx *Context) Object() Value { 296 | return Value{ctx: ctx, ref: C.JS_NewObject(ctx.ref)} 297 | } 298 | 299 | func (ctx *Context) Array() Value { 300 | return Value{ctx: ctx, ref: C.JS_NewArray(ctx.ref)} 301 | } 302 | 303 | type Atom struct { 304 | ctx *Context 305 | ref C.JSAtom 306 | } 307 | 308 | func (a Atom) Free() { C.JS_FreeAtom(a.ctx.ref, a.ref) } 309 | 310 | func (a Atom) String() string { 311 | ptr := C.JS_AtomToCString(a.ctx.ref, a.ref) 312 | defer C.JS_FreeCString(a.ctx.ref, ptr) 313 | return C.GoString(ptr) 314 | } 315 | 316 | func (a Atom) Value() Value { 317 | return Value{ctx: a.ctx, ref: C.JS_AtomToValue(a.ctx.ref, a.ref)} 318 | } 319 | 320 | type Value struct { 321 | ctx *Context 322 | ref C.JSValue 323 | } 324 | 325 | func (v Value) Free() { C.JS_FreeValue(v.ctx.ref, v.ref) } 326 | 327 | func (v Value) Context() *Context { return v.ctx } 328 | 329 | func (v Value) Bool() bool { return C.JS_ToBool(v.ctx.ref, v.ref) == 1 } 330 | 331 | func (v Value) String() string { 332 | ptr := C.JS_ToCString(v.ctx.ref, v.ref) 333 | defer C.JS_FreeCString(v.ctx.ref, ptr) 334 | return C.GoString(ptr) 335 | } 336 | 337 | func (v Value) Int64() int64 { 338 | val := C.int64_t(0) 339 | C.JS_ToInt64(v.ctx.ref, &val, v.ref) 340 | return int64(val) 341 | } 342 | 343 | func (v Value) Int32() int32 { 344 | val := C.int32_t(0) 345 | C.JS_ToInt32(v.ctx.ref, &val, v.ref) 346 | return int32(val) 347 | } 348 | 349 | func (v Value) Uint32() uint32 { 350 | val := C.uint32_t(0) 351 | C.JS_ToUint32(v.ctx.ref, &val, v.ref) 352 | return uint32(val) 353 | } 354 | 355 | func (v Value) Float64() float64 { 356 | val := C.double(0) 357 | C.JS_ToFloat64(v.ctx.ref, &val, v.ref) 358 | return float64(val) 359 | } 360 | 361 | func (v Value) BigInt() *big.Int { 362 | if !v.IsBigInt() { 363 | return nil 364 | } 365 | val, ok := new(big.Int).SetString(v.String(), 10) 366 | if !ok { 367 | return nil 368 | } 369 | return val 370 | } 371 | 372 | func (v Value) BigFloat() *big.Float { 373 | if !v.IsBigDecimal() && !v.IsBigFloat() { 374 | return nil 375 | } 376 | val, ok := new(big.Float).SetString(v.String()) 377 | if !ok { 378 | return nil 379 | } 380 | return val 381 | } 382 | 383 | func (v Value) Get(name string) Value { 384 | namePtr := C.CString(name) 385 | defer C.free(unsafe.Pointer(namePtr)) 386 | return Value{ctx: v.ctx, ref: C.JS_GetPropertyStr(v.ctx.ref, v.ref, namePtr)} 387 | } 388 | 389 | func (v Value) GetByAtom(atom Atom) Value { 390 | return Value{ctx: v.ctx, ref: C.JS_GetProperty(v.ctx.ref, v.ref, atom.ref)} 391 | } 392 | 393 | func (v Value) GetByUint32(idx uint32) Value { 394 | return Value{ctx: v.ctx, ref: C.JS_GetPropertyUint32(v.ctx.ref, v.ref, C.uint32_t(idx))} 395 | } 396 | 397 | func (v Value) SetByAtom(atom Atom, val Value) { 398 | C.JS_SetProperty(v.ctx.ref, v.ref, atom.ref, val.ref) 399 | } 400 | 401 | func (v Value) SetByInt64(idx int64, val Value) { 402 | C.JS_SetPropertyInt64(v.ctx.ref, v.ref, C.int64_t(idx), val.ref) 403 | } 404 | 405 | func (v Value) SetByUint32(idx uint32, val Value) { 406 | C.JS_SetPropertyUint32(v.ctx.ref, v.ref, C.uint32_t(idx), val.ref) 407 | } 408 | 409 | func (v Value) Len() int64 { return v.Get("length").Int64() } 410 | 411 | func (v Value) Set(name string, val Value) { 412 | namePtr := C.CString(name) 413 | defer C.free(unsafe.Pointer(namePtr)) 414 | C.JS_SetPropertyStr(v.ctx.ref, v.ref, namePtr, val.ref) 415 | } 416 | 417 | func (v Value) SetFunction(name string, fn Function) { 418 | v.Set(name, v.ctx.Function(fn)) 419 | } 420 | 421 | type Error struct { 422 | Cause string 423 | Stack string 424 | } 425 | 426 | func (err Error) Error() string { return err.Cause } 427 | 428 | func (v Value) Error() error { 429 | if !v.IsError() { 430 | return nil 431 | } 432 | cause := v.String() 433 | 434 | stack := v.Get("stack") 435 | defer stack.Free() 436 | 437 | if stack.IsUndefined() { 438 | return &Error{Cause: cause} 439 | } 440 | return &Error{Cause: cause, Stack: stack.String()} 441 | } 442 | 443 | func (v Value) IsNumber() bool { return C.JS_IsNumber(v.ref) == 1 } 444 | func (v Value) IsBigInt() bool { return C.JS_IsBigInt(v.ctx.ref, v.ref) == 1 } 445 | func (v Value) IsBigFloat() bool { return C.JS_IsBigFloat(v.ref) == 1 } 446 | func (v Value) IsBigDecimal() bool { return C.JS_IsBigDecimal(v.ref) == 1 } 447 | func (v Value) IsBool() bool { return C.JS_IsBool(v.ref) == 1 } 448 | func (v Value) IsNull() bool { return C.JS_IsNull(v.ref) == 1 } 449 | func (v Value) IsUndefined() bool { return C.JS_IsUndefined(v.ref) == 1 } 450 | func (v Value) IsException() bool { return C.JS_IsException(v.ref) == 1 } 451 | func (v Value) IsUninitialized() bool { return C.JS_IsUninitialized(v.ref) == 1 } 452 | func (v Value) IsString() bool { return C.JS_IsString(v.ref) == 1 } 453 | func (v Value) IsSymbol() bool { return C.JS_IsSymbol(v.ref) == 1 } 454 | func (v Value) IsObject() bool { return C.JS_IsObject(v.ref) == 1 } 455 | func (v Value) IsArray() bool { return C.JS_IsArray(v.ctx.ref, v.ref) == 1 } 456 | 457 | func (v Value) IsError() bool { return C.JS_IsError(v.ctx.ref, v.ref) == 1 } 458 | func (v Value) IsFunction() bool { return C.JS_IsFunction(v.ctx.ref, v.ref) == 1 } 459 | func (v Value) IsConstructor() bool { return C.JS_IsConstructor(v.ctx.ref, v.ref) == 1 } 460 | 461 | type PropertyEnum struct { 462 | IsEnumerable bool 463 | Atom Atom 464 | } 465 | 466 | func (p PropertyEnum) String() string { return p.Atom.String() } 467 | 468 | func (v Value) PropertyNames() ([]PropertyEnum, error) { 469 | var ( 470 | ptr *C.JSPropertyEnum 471 | size C.uint32_t 472 | ) 473 | 474 | result := int(C.JS_GetOwnPropertyNames(v.ctx.ref, &ptr, &size, v.ref, C.int(1<<0|1<<1|1<<2))) 475 | if result < 0 { 476 | return nil, errors.New("value does not contain properties") 477 | } 478 | defer C.js_free(v.ctx.ref, unsafe.Pointer(ptr)) 479 | 480 | entries := (*[(1 << 29) - 1]C.JSPropertyEnum)(unsafe.Pointer(ptr)) 481 | 482 | names := make([]PropertyEnum, uint32(size)) 483 | 484 | for i := 0; i < len(names); i++ { 485 | names[i].IsEnumerable = entries[i].is_enumerable == 1 486 | 487 | names[i].Atom = Atom{ctx: v.ctx, ref: entries[i].atom} 488 | names[i].Atom.Free() 489 | } 490 | 491 | return names, nil 492 | } 493 | -------------------------------------------------------------------------------- /quickjs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS Javascript Engine 3 | * 4 | * Copyright (c) 2017-2020 Fabrice Bellard 5 | * Copyright (c) 2017-2020 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | #ifndef QUICKJS_H 26 | #define QUICKJS_H 27 | 28 | #include 29 | #include 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | #if defined(__GNUC__) || defined(__clang__) 36 | #define js_likely(x) __builtin_expect(!!(x), 1) 37 | #define js_unlikely(x) __builtin_expect(!!(x), 0) 38 | #define js_force_inline inline __attribute__((always_inline)) 39 | #define __js_printf_like(f, a) __attribute__((format(printf, f, a))) 40 | #else 41 | #define js_likely(x) (x) 42 | #define js_unlikely(x) (x) 43 | #define js_force_inline inline 44 | #define __js_printf_like(a, b) 45 | #endif 46 | 47 | #define JS_BOOL int 48 | 49 | typedef struct JSRuntime JSRuntime; 50 | typedef struct JSContext JSContext; 51 | typedef struct JSObject JSObject; 52 | typedef struct JSClass JSClass; 53 | typedef uint32_t JSClassID; 54 | typedef uint32_t JSAtom; 55 | 56 | #if INTPTR_MAX >= INT64_MAX 57 | #define JS_PTR64 58 | #define JS_PTR64_DEF(a) a 59 | #else 60 | #define JS_PTR64_DEF(a) 61 | #endif 62 | 63 | #ifndef JS_PTR64 64 | #define JS_NAN_BOXING 65 | #endif 66 | 67 | enum { 68 | /* all tags with a reference count are negative */ 69 | JS_TAG_FIRST = -11, /* first negative tag */ 70 | JS_TAG_BIG_DECIMAL = -11, 71 | JS_TAG_BIG_INT = -10, 72 | JS_TAG_BIG_FLOAT = -9, 73 | JS_TAG_SYMBOL = -8, 74 | JS_TAG_STRING = -7, 75 | JS_TAG_MODULE = -3, /* used internally */ 76 | JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ 77 | JS_TAG_OBJECT = -1, 78 | 79 | JS_TAG_INT = 0, 80 | JS_TAG_BOOL = 1, 81 | JS_TAG_NULL = 2, 82 | JS_TAG_UNDEFINED = 3, 83 | JS_TAG_UNINITIALIZED = 4, 84 | JS_TAG_CATCH_OFFSET = 5, 85 | JS_TAG_EXCEPTION = 6, 86 | JS_TAG_FLOAT64 = 7, 87 | /* any larger tag is FLOAT64 if JS_NAN_BOXING */ 88 | }; 89 | 90 | typedef struct JSRefCountHeader { 91 | int ref_count; 92 | } JSRefCountHeader; 93 | 94 | #define JS_FLOAT64_NAN NAN 95 | 96 | #ifdef CONFIG_CHECK_JSVALUE 97 | /* JSValue consistency : it is not possible to run the code in this 98 | mode, but it is useful to detect simple reference counting 99 | errors. It would be interesting to modify a static C analyzer to 100 | handle specific annotations (clang has such annotations but only 101 | for objective C) */ 102 | typedef struct __JSValue *JSValue; 103 | typedef const struct __JSValue *JSValueConst; 104 | 105 | #define JS_VALUE_GET_TAG(v) (int)((uintptr_t)(v) & 0xf) 106 | /* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ 107 | #define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) 108 | #define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4) 109 | #define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v) 110 | #define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v) 111 | #define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf) 112 | 113 | #define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag)) 114 | #define JS_MKPTR(tag, p) (JSValue)((intptr_t)(p) | (tag)) 115 | 116 | #define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) 117 | 118 | #define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 1) 119 | 120 | static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) 121 | { 122 | return JS_MKVAL(JS_TAG_FLOAT64, (int)d); 123 | } 124 | 125 | static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) 126 | { 127 | return 0; 128 | } 129 | 130 | #elif defined(JS_NAN_BOXING) 131 | 132 | typedef uint64_t JSValue; 133 | 134 | #define JSValueConst JSValue 135 | 136 | #define JS_VALUE_GET_TAG(v) (int)((v) >> 32) 137 | #define JS_VALUE_GET_INT(v) (int)(v) 138 | #define JS_VALUE_GET_BOOL(v) (int)(v) 139 | #define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v) 140 | 141 | #define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) 142 | #define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr)) 143 | 144 | #define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */ 145 | 146 | static inline double JS_VALUE_GET_FLOAT64(JSValue v) 147 | { 148 | union { 149 | JSValue v; 150 | double d; 151 | } u; 152 | u.v = v; 153 | u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32; 154 | return u.d; 155 | } 156 | 157 | #define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32)) 158 | 159 | static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) 160 | { 161 | union { 162 | double d; 163 | uint64_t u64; 164 | } u; 165 | JSValue v; 166 | u.d = d; 167 | /* normalize NaN */ 168 | if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000)) 169 | v = JS_NAN; 170 | else 171 | v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32); 172 | return v; 173 | } 174 | 175 | #define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag) - JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST)) 176 | 177 | /* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ 178 | static inline int JS_VALUE_GET_NORM_TAG(JSValue v) 179 | { 180 | uint32_t tag; 181 | tag = JS_VALUE_GET_TAG(v); 182 | if (JS_TAG_IS_FLOAT64(tag)) 183 | return JS_TAG_FLOAT64; 184 | else 185 | return tag; 186 | } 187 | 188 | static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) 189 | { 190 | uint32_t tag; 191 | tag = JS_VALUE_GET_TAG(v); 192 | return tag == (JS_NAN >> 32); 193 | } 194 | 195 | #else /* !JS_NAN_BOXING */ 196 | 197 | typedef union JSValueUnion { 198 | int32_t int32; 199 | double float64; 200 | void *ptr; 201 | } JSValueUnion; 202 | 203 | typedef struct JSValue { 204 | JSValueUnion u; 205 | int64_t tag; 206 | } JSValue; 207 | 208 | #define JSValueConst JSValue 209 | 210 | #define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag) 211 | /* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ 212 | #define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) 213 | #define JS_VALUE_GET_INT(v) ((v).u.int32) 214 | #define JS_VALUE_GET_BOOL(v) ((v).u.int32) 215 | #define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) 216 | #define JS_VALUE_GET_PTR(v) ((v).u.ptr) 217 | 218 | #define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } 219 | #define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } 220 | 221 | #define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) 222 | 223 | #define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } 224 | 225 | static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) 226 | { 227 | JSValue v; 228 | v.tag = JS_TAG_FLOAT64; 229 | v.u.float64 = d; 230 | return v; 231 | } 232 | 233 | static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) 234 | { 235 | union { 236 | double d; 237 | uint64_t u64; 238 | } u; 239 | if (v.tag != JS_TAG_FLOAT64) 240 | return 0; 241 | u.d = v.u.float64; 242 | return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000; 243 | } 244 | 245 | #endif /* !JS_NAN_BOXING */ 246 | 247 | #define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0) 248 | #define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2))) 249 | 250 | #define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v)) 251 | #define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) 252 | #define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST) 253 | 254 | /* special values */ 255 | #define JS_NULL JS_MKVAL(JS_TAG_NULL, 0) 256 | #define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0) 257 | #define JS_FALSE JS_MKVAL(JS_TAG_BOOL, 0) 258 | #define JS_TRUE JS_MKVAL(JS_TAG_BOOL, 1) 259 | #define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0) 260 | #define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0) 261 | 262 | /* flags for object properties */ 263 | #define JS_PROP_CONFIGURABLE (1 << 0) 264 | #define JS_PROP_WRITABLE (1 << 1) 265 | #define JS_PROP_ENUMERABLE (1 << 2) 266 | #define JS_PROP_C_W_E (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE) 267 | #define JS_PROP_LENGTH (1 << 3) /* used internally in Arrays */ 268 | #define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */ 269 | #define JS_PROP_NORMAL (0 << 4) 270 | #define JS_PROP_GETSET (1 << 4) 271 | #define JS_PROP_VARREF (2 << 4) /* used internally */ 272 | #define JS_PROP_AUTOINIT (3 << 4) /* used internally */ 273 | 274 | /* flags for JS_DefineProperty */ 275 | #define JS_PROP_HAS_SHIFT 8 276 | #define JS_PROP_HAS_CONFIGURABLE (1 << 8) 277 | #define JS_PROP_HAS_WRITABLE (1 << 9) 278 | #define JS_PROP_HAS_ENUMERABLE (1 << 10) 279 | #define JS_PROP_HAS_GET (1 << 11) 280 | #define JS_PROP_HAS_SET (1 << 12) 281 | #define JS_PROP_HAS_VALUE (1 << 13) 282 | 283 | /* throw an exception if false would be returned 284 | (JS_DefineProperty/JS_SetProperty) */ 285 | #define JS_PROP_THROW (1 << 14) 286 | /* throw an exception if false would be returned in strict mode 287 | (JS_SetProperty) */ 288 | #define JS_PROP_THROW_STRICT (1 << 15) 289 | 290 | #define JS_PROP_NO_ADD (1 << 16) /* internal use */ 291 | #define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */ 292 | 293 | #define JS_DEFAULT_STACK_SIZE (256 * 1024) 294 | 295 | /* JS_Eval() flags */ 296 | #define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */ 297 | #define JS_EVAL_TYPE_MODULE (1 << 0) /* module code */ 298 | #define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */ 299 | #define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */ 300 | #define JS_EVAL_TYPE_MASK (3 << 0) 301 | 302 | #define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */ 303 | #define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */ 304 | /* compile but do not run. The result is an object with a 305 | JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed 306 | with JS_EvalFunction(). */ 307 | #define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) 308 | /* don't include the stack frames before this eval in the Error() backtraces */ 309 | #define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) 310 | 311 | typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); 312 | typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); 313 | typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data); 314 | 315 | typedef struct JSMallocState { 316 | size_t malloc_count; 317 | size_t malloc_size; 318 | size_t malloc_limit; 319 | void *opaque; /* user opaque */ 320 | } JSMallocState; 321 | 322 | typedef struct JSMallocFunctions { 323 | void *(*js_malloc)(JSMallocState *s, size_t size); 324 | void (*js_free)(JSMallocState *s, void *ptr); 325 | void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size); 326 | size_t (*js_malloc_usable_size)(const void *ptr); 327 | } JSMallocFunctions; 328 | 329 | typedef struct JSGCObjectHeader JSGCObjectHeader; 330 | 331 | JSRuntime *JS_NewRuntime(void); 332 | /* info lifetime must exceed that of rt */ 333 | void JS_SetRuntimeInfo(JSRuntime *rt, const char *info); 334 | void JS_SetMemoryLimit(JSRuntime *rt, size_t limit); 335 | void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold); 336 | void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size); 337 | JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque); 338 | void JS_FreeRuntime(JSRuntime *rt); 339 | void *JS_GetRuntimeOpaque(JSRuntime *rt); 340 | void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque); 341 | typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp); 342 | void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); 343 | void JS_RunGC(JSRuntime *rt); 344 | JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj); 345 | 346 | JSContext *JS_NewContext(JSRuntime *rt); 347 | void JS_FreeContext(JSContext *s); 348 | JSContext *JS_DupContext(JSContext *ctx); 349 | void *JS_GetContextOpaque(JSContext *ctx); 350 | void JS_SetContextOpaque(JSContext *ctx, void *opaque); 351 | JSRuntime *JS_GetRuntime(JSContext *ctx); 352 | void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj); 353 | JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id); 354 | 355 | /* the following functions are used to select the intrinsic object to 356 | save memory */ 357 | JSContext *JS_NewContextRaw(JSRuntime *rt); 358 | void JS_AddIntrinsicBaseObjects(JSContext *ctx); 359 | void JS_AddIntrinsicDate(JSContext *ctx); 360 | void JS_AddIntrinsicEval(JSContext *ctx); 361 | void JS_AddIntrinsicStringNormalize(JSContext *ctx); 362 | void JS_AddIntrinsicRegExpCompiler(JSContext *ctx); 363 | void JS_AddIntrinsicRegExp(JSContext *ctx); 364 | void JS_AddIntrinsicJSON(JSContext *ctx); 365 | void JS_AddIntrinsicProxy(JSContext *ctx); 366 | void JS_AddIntrinsicMapSet(JSContext *ctx); 367 | void JS_AddIntrinsicTypedArrays(JSContext *ctx); 368 | void JS_AddIntrinsicPromise(JSContext *ctx); 369 | void JS_AddIntrinsicBigInt(JSContext *ctx); 370 | void JS_AddIntrinsicBigFloat(JSContext *ctx); 371 | void JS_AddIntrinsicBigDecimal(JSContext *ctx); 372 | /* enable operator overloading */ 373 | void JS_AddIntrinsicOperators(JSContext *ctx); 374 | /* enable "use math" */ 375 | void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable); 376 | 377 | JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, 378 | int argc, JSValueConst *argv); 379 | 380 | void *js_malloc_rt(JSRuntime *rt, size_t size); 381 | void js_free_rt(JSRuntime *rt, void *ptr); 382 | void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size); 383 | size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr); 384 | void *js_mallocz_rt(JSRuntime *rt, size_t size); 385 | 386 | void *js_malloc(JSContext *ctx, size_t size); 387 | void js_free(JSContext *ctx, void *ptr); 388 | void *js_realloc(JSContext *ctx, void *ptr, size_t size); 389 | size_t js_malloc_usable_size(JSContext *ctx, const void *ptr); 390 | void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack); 391 | void *js_mallocz(JSContext *ctx, size_t size); 392 | char *js_strdup(JSContext *ctx, const char *str); 393 | char *js_strndup(JSContext *ctx, const char *s, size_t n); 394 | 395 | typedef struct JSMemoryUsage { 396 | int64_t malloc_size, malloc_limit, memory_used_size; 397 | int64_t malloc_count; 398 | int64_t memory_used_count; 399 | int64_t atom_count, atom_size; 400 | int64_t str_count, str_size; 401 | int64_t obj_count, obj_size; 402 | int64_t prop_count, prop_size; 403 | int64_t shape_count, shape_size; 404 | int64_t js_func_count, js_func_size, js_func_code_size; 405 | int64_t js_func_pc2line_count, js_func_pc2line_size; 406 | int64_t c_func_count, array_count; 407 | int64_t fast_array_count, fast_array_elements; 408 | int64_t binary_object_count, binary_object_size; 409 | } JSMemoryUsage; 410 | 411 | void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s); 412 | void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt); 413 | 414 | /* atom support */ 415 | JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len); 416 | JSAtom JS_NewAtom(JSContext *ctx, const char *str); 417 | JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n); 418 | JSAtom JS_DupAtom(JSContext *ctx, JSAtom v); 419 | void JS_FreeAtom(JSContext *ctx, JSAtom v); 420 | void JS_FreeAtomRT(JSRuntime *rt, JSAtom v); 421 | JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom); 422 | JSValue JS_AtomToString(JSContext *ctx, JSAtom atom); 423 | const char *JS_AtomToCString(JSContext *ctx, JSAtom atom); 424 | JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val); 425 | 426 | /* object class support */ 427 | 428 | typedef struct JSPropertyEnum { 429 | JS_BOOL is_enumerable; 430 | JSAtom atom; 431 | } JSPropertyEnum; 432 | 433 | typedef struct JSPropertyDescriptor { 434 | int flags; 435 | JSValue value; 436 | JSValue getter; 437 | JSValue setter; 438 | } JSPropertyDescriptor; 439 | 440 | typedef struct JSClassExoticMethods { 441 | /* Return -1 if exception (can only happen in case of Proxy object), 442 | FALSE if the property does not exists, TRUE if it exists. If 1 is 443 | returned, the property descriptor 'desc' is filled if != NULL. */ 444 | int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc, 445 | JSValueConst obj, JSAtom prop); 446 | /* '*ptab' should hold the '*plen' property keys. Return 0 if OK, 447 | -1 if exception. The 'is_enumerable' field is ignored. 448 | */ 449 | int (*get_own_property_names)(JSContext *ctx, JSPropertyEnum **ptab, 450 | uint32_t *plen, 451 | JSValueConst obj); 452 | /* return < 0 if exception, or TRUE/FALSE */ 453 | int (*delete_property)(JSContext *ctx, JSValueConst obj, JSAtom prop); 454 | /* return < 0 if exception or TRUE/FALSE */ 455 | int (*define_own_property)(JSContext *ctx, JSValueConst this_obj, 456 | JSAtom prop, JSValueConst val, 457 | JSValueConst getter, JSValueConst setter, 458 | int flags); 459 | /* The following methods can be emulated with the previous ones, 460 | so they are usually not needed */ 461 | /* return < 0 if exception or TRUE/FALSE */ 462 | int (*has_property)(JSContext *ctx, JSValueConst obj, JSAtom atom); 463 | JSValue (*get_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, 464 | JSValueConst receiver); 465 | /* return < 0 if exception or TRUE/FALSE */ 466 | int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, 467 | JSValueConst value, JSValueConst receiver, int flags); 468 | } JSClassExoticMethods; 469 | 470 | typedef void JSClassFinalizer(JSRuntime *rt, JSValue val); 471 | typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val, 472 | JS_MarkFunc *mark_func); 473 | #define JS_CALL_FLAG_CONSTRUCTOR (1 << 0) 474 | typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj, 475 | JSValueConst this_val, int argc, JSValueConst *argv, 476 | int flags); 477 | 478 | typedef struct JSClassDef { 479 | const char *class_name; 480 | JSClassFinalizer *finalizer; 481 | JSClassGCMark *gc_mark; 482 | /* if call != NULL, the object is a function. If (flags & 483 | JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a 484 | constructor. In this case, 'this_val' is new.target. A 485 | constructor call only happens if the object constructor bit is 486 | set (see JS_SetConstructorBit()). */ 487 | JSClassCall *call; 488 | /* XXX: suppress this indirection ? It is here only to save memory 489 | because only a few classes need these methods */ 490 | JSClassExoticMethods *exotic; 491 | } JSClassDef; 492 | 493 | JSClassID JS_NewClassID(JSClassID *pclass_id); 494 | int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def); 495 | int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); 496 | 497 | /* value handling */ 498 | 499 | static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) 500 | { 501 | return JS_MKVAL(JS_TAG_BOOL, (val != 0)); 502 | } 503 | 504 | static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val) 505 | { 506 | return JS_MKVAL(JS_TAG_INT, val); 507 | } 508 | 509 | static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) 510 | { 511 | return JS_MKVAL(JS_TAG_CATCH_OFFSET, val); 512 | } 513 | 514 | static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) 515 | { 516 | JSValue v; 517 | if (val == (int32_t)val) { 518 | v = JS_NewInt32(ctx, val); 519 | } else { 520 | v = __JS_NewFloat64(ctx, val); 521 | } 522 | return v; 523 | } 524 | 525 | static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) 526 | { 527 | JSValue v; 528 | if (val <= 0x7fffffff) { 529 | v = JS_NewInt32(ctx, val); 530 | } else { 531 | v = __JS_NewFloat64(ctx, val); 532 | } 533 | return v; 534 | } 535 | 536 | JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); 537 | JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); 538 | 539 | static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d) 540 | { 541 | JSValue v; 542 | int32_t val; 543 | union { 544 | double d; 545 | uint64_t u; 546 | } u, t; 547 | u.d = d; 548 | val = (int32_t)d; 549 | t.d = val; 550 | /* -0 cannot be represented as integer, so we compare the bit 551 | representation */ 552 | if (u.u == t.u) { 553 | v = JS_MKVAL(JS_TAG_INT, val); 554 | } else { 555 | v = __JS_NewFloat64(ctx, d); 556 | } 557 | return v; 558 | } 559 | 560 | static inline JS_BOOL JS_IsNumber(JSValueConst v) 561 | { 562 | int tag = JS_VALUE_GET_TAG(v); 563 | return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); 564 | } 565 | 566 | static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) 567 | { 568 | int tag = JS_VALUE_GET_TAG(v); 569 | return tag == JS_TAG_BIG_INT; 570 | } 571 | 572 | static inline JS_BOOL JS_IsBigFloat(JSValueConst v) 573 | { 574 | int tag = JS_VALUE_GET_TAG(v); 575 | return tag == JS_TAG_BIG_FLOAT; 576 | } 577 | 578 | static inline JS_BOOL JS_IsBigDecimal(JSValueConst v) 579 | { 580 | int tag = JS_VALUE_GET_TAG(v); 581 | return tag == JS_TAG_BIG_DECIMAL; 582 | } 583 | 584 | static inline JS_BOOL JS_IsBool(JSValueConst v) 585 | { 586 | return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL; 587 | } 588 | 589 | static inline JS_BOOL JS_IsNull(JSValueConst v) 590 | { 591 | return JS_VALUE_GET_TAG(v) == JS_TAG_NULL; 592 | } 593 | 594 | static inline JS_BOOL JS_IsUndefined(JSValueConst v) 595 | { 596 | return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED; 597 | } 598 | 599 | static inline JS_BOOL JS_IsException(JSValueConst v) 600 | { 601 | return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION); 602 | } 603 | 604 | static inline JS_BOOL JS_IsUninitialized(JSValueConst v) 605 | { 606 | return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED); 607 | } 608 | 609 | static inline JS_BOOL JS_IsString(JSValueConst v) 610 | { 611 | return JS_VALUE_GET_TAG(v) == JS_TAG_STRING; 612 | } 613 | 614 | static inline JS_BOOL JS_IsSymbol(JSValueConst v) 615 | { 616 | return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL; 617 | } 618 | 619 | static inline JS_BOOL JS_IsObject(JSValueConst v) 620 | { 621 | return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT; 622 | } 623 | 624 | JSValue JS_Throw(JSContext *ctx, JSValue obj); 625 | JSValue JS_GetException(JSContext *ctx); 626 | JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); 627 | void JS_ResetUncatchableError(JSContext *ctx); 628 | JSValue JS_NewError(JSContext *ctx); 629 | JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); 630 | JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...); 631 | JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...); 632 | JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...); 633 | JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); 634 | JSValue JS_ThrowOutOfMemory(JSContext *ctx); 635 | 636 | void __JS_FreeValue(JSContext *ctx, JSValue v); 637 | static inline void JS_FreeValue(JSContext *ctx, JSValue v) 638 | { 639 | if (JS_VALUE_HAS_REF_COUNT(v)) { 640 | JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); 641 | if (--p->ref_count <= 0) { 642 | __JS_FreeValue(ctx, v); 643 | } 644 | } 645 | } 646 | void __JS_FreeValueRT(JSRuntime *rt, JSValue v); 647 | static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v) 648 | { 649 | if (JS_VALUE_HAS_REF_COUNT(v)) { 650 | JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); 651 | if (--p->ref_count <= 0) { 652 | __JS_FreeValueRT(rt, v); 653 | } 654 | } 655 | } 656 | 657 | static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v) 658 | { 659 | if (JS_VALUE_HAS_REF_COUNT(v)) { 660 | JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); 661 | p->ref_count++; 662 | } 663 | return (JSValue)v; 664 | } 665 | 666 | static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) 667 | { 668 | if (JS_VALUE_HAS_REF_COUNT(v)) { 669 | JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); 670 | p->ref_count++; 671 | } 672 | return (JSValue)v; 673 | } 674 | 675 | int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ 676 | int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val); 677 | static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val) 678 | { 679 | return JS_ToInt32(ctx, (int32_t*)pres, val); 680 | } 681 | int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val); 682 | int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val); 683 | int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val); 684 | /* return an exception if 'val' is a Number */ 685 | int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val); 686 | /* same as JS_ToInt64() but allow BigInt */ 687 | int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val); 688 | 689 | JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); 690 | JSValue JS_NewString(JSContext *ctx, const char *str); 691 | JSValue JS_NewAtomString(JSContext *ctx, const char *str); 692 | JSValue JS_ToString(JSContext *ctx, JSValueConst val); 693 | JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val); 694 | const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, JS_BOOL cesu8); 695 | static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1) 696 | { 697 | return JS_ToCStringLen2(ctx, plen, val1, 0); 698 | } 699 | static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1) 700 | { 701 | return JS_ToCStringLen2(ctx, NULL, val1, 0); 702 | } 703 | void JS_FreeCString(JSContext *ctx, const char *ptr); 704 | 705 | JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id); 706 | JSValue JS_NewObjectClass(JSContext *ctx, int class_id); 707 | JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto); 708 | JSValue JS_NewObject(JSContext *ctx); 709 | 710 | JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val); 711 | JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val); 712 | JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val); 713 | 714 | JSValue JS_NewArray(JSContext *ctx); 715 | int JS_IsArray(JSContext *ctx, JSValueConst val); 716 | 717 | JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, 718 | JSAtom prop, JSValueConst receiver, 719 | JS_BOOL throw_ref_error); 720 | static js_force_inline JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj, 721 | JSAtom prop) 722 | { 723 | return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0); 724 | } 725 | JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, 726 | const char *prop); 727 | JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, 728 | uint32_t idx); 729 | 730 | int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, 731 | JSAtom prop, JSValue val, 732 | int flags); 733 | static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj, 734 | JSAtom prop, JSValue val) 735 | { 736 | return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW); 737 | } 738 | int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, 739 | uint32_t idx, JSValue val); 740 | int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, 741 | int64_t idx, JSValue val); 742 | int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, 743 | const char *prop, JSValue val); 744 | int JS_HasProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop); 745 | int JS_IsExtensible(JSContext *ctx, JSValueConst obj); 746 | int JS_PreventExtensions(JSContext *ctx, JSValueConst obj); 747 | int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags); 748 | int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val); 749 | JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val); 750 | 751 | #define JS_GPN_STRING_MASK (1 << 0) 752 | #define JS_GPN_SYMBOL_MASK (1 << 1) 753 | #define JS_GPN_PRIVATE_MASK (1 << 2) 754 | /* only include the enumerable properties */ 755 | #define JS_GPN_ENUM_ONLY (1 << 4) 756 | /* set theJSPropertyEnum.is_enumerable field */ 757 | #define JS_GPN_SET_ENUM (1 << 5) 758 | 759 | int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, 760 | uint32_t *plen, JSValueConst obj, int flags); 761 | int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, 762 | JSValueConst obj, JSAtom prop); 763 | 764 | JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, 765 | int argc, JSValueConst *argv); 766 | JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, 767 | int argc, JSValueConst *argv); 768 | JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, 769 | int argc, JSValueConst *argv); 770 | JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, 771 | JSValueConst new_target, 772 | int argc, JSValueConst *argv); 773 | JS_BOOL JS_DetectModule(const char *input, size_t input_len); 774 | /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ 775 | JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, 776 | const char *filename, int eval_flags); 777 | JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj); 778 | JSValue JS_GetGlobalObject(JSContext *ctx); 779 | int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj); 780 | int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, 781 | JSAtom prop, JSValueConst val, 782 | JSValueConst getter, JSValueConst setter, int flags); 783 | int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, 784 | JSAtom prop, JSValue val, int flags); 785 | int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, 786 | uint32_t idx, JSValue val, int flags); 787 | int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, 788 | const char *prop, JSValue val, int flags); 789 | int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, 790 | JSAtom prop, JSValue getter, JSValue setter, 791 | int flags); 792 | void JS_SetOpaque(JSValue obj, void *opaque); 793 | void *JS_GetOpaque(JSValueConst obj, JSClassID class_id); 794 | void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id); 795 | 796 | /* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ 797 | JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, 798 | const char *filename); 799 | #define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */ 800 | JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, 801 | const char *filename, int flags); 802 | JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, 803 | JSValueConst replacer, JSValueConst space0); 804 | 805 | typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr); 806 | JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, 807 | JSFreeArrayBufferDataFunc *free_func, void *opaque, 808 | JS_BOOL is_shared); 809 | JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); 810 | void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); 811 | uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj); 812 | JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, 813 | size_t *pbyte_offset, 814 | size_t *pbyte_length, 815 | size_t *pbytes_per_element); 816 | typedef struct { 817 | void *(*sab_alloc)(void *opaque, size_t size); 818 | void (*sab_free)(void *opaque, void *ptr); 819 | void (*sab_dup)(void *opaque, void *ptr); 820 | void *sab_opaque; 821 | } JSSharedArrayBufferFunctions; 822 | void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, 823 | const JSSharedArrayBufferFunctions *sf); 824 | 825 | JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs); 826 | 827 | /* is_handled = TRUE means that the rejection is handled */ 828 | typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise, 829 | JSValueConst reason, 830 | JS_BOOL is_handled, void *opaque); 831 | void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque); 832 | 833 | /* return != 0 if the JS code needs to be interrupted */ 834 | typedef int JSInterruptHandler(JSRuntime *rt, void *opaque); 835 | void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque); 836 | /* if can_block is TRUE, Atomics.wait() can be used */ 837 | void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block); 838 | 839 | typedef struct JSModuleDef JSModuleDef; 840 | 841 | /* return the module specifier (allocated with js_malloc()) or NULL if 842 | exception */ 843 | typedef char *JSModuleNormalizeFunc(JSContext *ctx, 844 | const char *module_base_name, 845 | const char *module_name, void *opaque); 846 | typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx, 847 | const char *module_name, void *opaque); 848 | 849 | /* module_normalize = NULL is allowed and invokes the default module 850 | filename normalizer */ 851 | void JS_SetModuleLoaderFunc(JSRuntime *rt, 852 | JSModuleNormalizeFunc *module_normalize, 853 | JSModuleLoaderFunc *module_loader, void *opaque); 854 | /* return the import.meta object of a module */ 855 | JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m); 856 | JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m); 857 | 858 | /* JS Job support */ 859 | 860 | typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv); 861 | int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv); 862 | 863 | JS_BOOL JS_IsJobPending(JSRuntime *rt); 864 | int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx); 865 | 866 | /* Object Writer/Reader (currently only used to handle precompiled code) */ 867 | #define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ 868 | #define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */ 869 | #define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ 870 | #define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to 871 | encode arbitrary object 872 | graph */ 873 | uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, 874 | int flags); 875 | uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, 876 | int flags, uint8_t ***psab_tab, size_t *psab_tab_len); 877 | 878 | #define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */ 879 | #define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */ 880 | #define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ 881 | #define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */ 882 | JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, 883 | int flags); 884 | 885 | /* load the dependencies of the module 'obj'. Useful when JS_ReadObject() 886 | returns a module. */ 887 | int JS_ResolveModule(JSContext *ctx, JSValueConst obj); 888 | 889 | /* C function definition */ 890 | typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ 891 | JS_CFUNC_generic, 892 | JS_CFUNC_generic_magic, 893 | JS_CFUNC_constructor, 894 | JS_CFUNC_constructor_magic, 895 | JS_CFUNC_constructor_or_func, 896 | JS_CFUNC_constructor_or_func_magic, 897 | JS_CFUNC_f_f, 898 | JS_CFUNC_f_f_f, 899 | JS_CFUNC_getter, 900 | JS_CFUNC_setter, 901 | JS_CFUNC_getter_magic, 902 | JS_CFUNC_setter_magic, 903 | JS_CFUNC_iterator_next, 904 | } JSCFunctionEnum; 905 | 906 | typedef union JSCFunctionType { 907 | JSCFunction *generic; 908 | JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); 909 | JSCFunction *constructor; 910 | JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic); 911 | JSCFunction *constructor_or_func; 912 | double (*f_f)(double); 913 | double (*f_f_f)(double, double); 914 | JSValue (*getter)(JSContext *ctx, JSValueConst this_val); 915 | JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val); 916 | JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic); 917 | JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic); 918 | JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val, 919 | int argc, JSValueConst *argv, int *pdone, int magic); 920 | } JSCFunctionType; 921 | 922 | JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, 923 | const char *name, 924 | int length, JSCFunctionEnum cproto, int magic); 925 | JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, 926 | int length, int magic, int data_len, 927 | JSValueConst *data); 928 | 929 | static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name, 930 | int length) 931 | { 932 | return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); 933 | } 934 | 935 | static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func, 936 | const char *name, 937 | int length, JSCFunctionEnum cproto, int magic) 938 | { 939 | return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic); 940 | } 941 | void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, 942 | JSValueConst proto); 943 | 944 | /* C property definition */ 945 | 946 | typedef struct JSCFunctionListEntry { 947 | const char *name; 948 | uint8_t prop_flags; 949 | uint8_t def_type; 950 | int16_t magic; 951 | union { 952 | struct { 953 | uint8_t length; /* XXX: should move outside union */ 954 | uint8_t cproto; /* XXX: should move outside union */ 955 | JSCFunctionType cfunc; 956 | } func; 957 | struct { 958 | JSCFunctionType get; 959 | JSCFunctionType set; 960 | } getset; 961 | struct { 962 | const char *name; 963 | int base; 964 | } alias; 965 | struct { 966 | const struct JSCFunctionListEntry *tab; 967 | int len; 968 | } prop_list; 969 | const char *str; 970 | int32_t i32; 971 | int64_t i64; 972 | double f64; 973 | } u; 974 | } JSCFunctionListEntry; 975 | 976 | #define JS_DEF_CFUNC 0 977 | #define JS_DEF_CGETSET 1 978 | #define JS_DEF_CGETSET_MAGIC 2 979 | #define JS_DEF_PROP_STRING 3 980 | #define JS_DEF_PROP_INT32 4 981 | #define JS_DEF_PROP_INT64 5 982 | #define JS_DEF_PROP_DOUBLE 6 983 | #define JS_DEF_PROP_UNDEFINED 7 984 | #define JS_DEF_OBJECT 8 985 | #define JS_DEF_ALIAS 9 986 | 987 | /* Note: c++ does not like nested designators */ 988 | #define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } 989 | #define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } } 990 | #define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } } 991 | #define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } } 992 | #define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } } 993 | #define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, .u = { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } } 994 | #define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, .u = { .str = cstr } } 995 | #define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, .u = { .i32 = val } } 996 | #define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } } 997 | #define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } } 998 | #define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } } 999 | #define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } } 1000 | #define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } } 1001 | #define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } } 1002 | 1003 | void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, 1004 | const JSCFunctionListEntry *tab, 1005 | int len); 1006 | 1007 | /* C module definition */ 1008 | 1009 | typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m); 1010 | 1011 | JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, 1012 | JSModuleInitFunc *func); 1013 | /* can only be called before the module is instantiated */ 1014 | int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *name_str); 1015 | int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, 1016 | const JSCFunctionListEntry *tab, int len); 1017 | /* can only be called after the module is instantiated */ 1018 | int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, 1019 | JSValue val); 1020 | int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, 1021 | const JSCFunctionListEntry *tab, int len); 1022 | 1023 | #undef js_unlikely 1024 | #undef js_force_inline 1025 | 1026 | #ifdef __cplusplus 1027 | } /* extern "C" { */ 1028 | #endif 1029 | 1030 | #endif /* QUICKJS_H */ 1031 | -------------------------------------------------------------------------------- /quickjs_test.go: -------------------------------------------------------------------------------- 1 | package quickjs 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/stretchr/testify/require" 7 | stdruntime "runtime" 8 | "sync" 9 | "testing" 10 | ) 11 | 12 | func TestObject(t *testing.T) { 13 | runtime := NewRuntime() 14 | defer runtime.Free() 15 | 16 | context := runtime.NewContext() 17 | defer context.Free() 18 | 19 | test := context.Object() 20 | test.Set("A", context.String("String A")) 21 | test.Set("B", context.String("String B")) 22 | test.Set("C", context.String("String C")) 23 | context.Globals().Set("test", test) 24 | 25 | result, err := context.Eval(`Object.keys(test).map(key => test[key]).join(" ")`) 26 | require.NoError(t, err) 27 | defer result.Free() 28 | 29 | require.EqualValues(t, "String A String B String C", result.String()) 30 | } 31 | 32 | func TestArray(t *testing.T) { 33 | runtime := NewRuntime() 34 | defer runtime.Free() 35 | 36 | context := runtime.NewContext() 37 | defer context.Free() 38 | 39 | test := context.Array() 40 | for i := int64(0); i < 3; i++ { 41 | test.SetByInt64(i, context.String(fmt.Sprintf("test %d", i))) 42 | } 43 | for i := int64(0); i < test.Len(); i++ { 44 | require.EqualValues(t, fmt.Sprintf("test %d", i), test.GetByUint32(uint32(i)).String()) 45 | } 46 | 47 | context.Globals().Set("test", test) 48 | 49 | result, err := context.Eval(`test.map(v => v.toUpperCase())`) 50 | require.NoError(t, err) 51 | defer result.Free() 52 | 53 | require.EqualValues(t, `TEST 0,TEST 1,TEST 2`, result.String()) 54 | } 55 | 56 | func TestBadSyntax(t *testing.T) { 57 | runtime := NewRuntime() 58 | defer runtime.Free() 59 | 60 | context := runtime.NewContext() 61 | defer context.Free() 62 | 63 | _, err := context.Eval(`"bad syntax'`) 64 | require.Error(t, err) 65 | } 66 | 67 | func TestFunctionThrowError(t *testing.T) { 68 | expected := errors.New("expected error") 69 | 70 | runtime := NewRuntime() 71 | defer runtime.Free() 72 | 73 | context := runtime.NewContext() 74 | defer context.Free() 75 | 76 | context.Globals().SetFunction("A", func(ctx *Context, this Value, args []Value) Value { 77 | return ctx.ThrowError(expected) 78 | }) 79 | 80 | _, actual := context.Eval("A()") 81 | require.Error(t, actual) 82 | require.EqualValues(t, "Error: "+expected.Error(), actual.Error()) 83 | } 84 | 85 | func TestFunction(t *testing.T) { 86 | runtime := NewRuntime() 87 | defer runtime.Free() 88 | 89 | context := runtime.NewContext() 90 | defer context.Free() 91 | 92 | A := make(chan struct{}) 93 | B := make(chan struct{}) 94 | 95 | context.Globals().SetFunction("A", func(ctx *Context, this Value, args []Value) Value { 96 | require.Len(t, args, 4) 97 | require.True(t, args[0].IsString() && args[0].String() == "hello world!") 98 | require.True(t, args[1].IsNumber() && args[1].Int32() == 1) 99 | require.True(t, args[2].IsNumber() && args[2].Int64() == 8) 100 | require.True(t, args[3].IsNull()) 101 | 102 | close(A) 103 | 104 | return ctx.String("A says hello") 105 | }) 106 | 107 | context.Globals().SetFunction("B", func(ctx *Context, this Value, args []Value) Value { 108 | require.Len(t, args, 0) 109 | 110 | close(B) 111 | 112 | return ctx.Float64(256) 113 | }) 114 | 115 | result, err := context.Eval(`A("hello world!", 1, 2 ** 3, null)`) 116 | require.NoError(t, err) 117 | defer result.Free() 118 | 119 | require.True(t, result.IsString() && result.String() == "A says hello") 120 | <-A 121 | 122 | result, err = context.Eval(`B()`) 123 | require.NoError(t, err) 124 | defer result.Free() 125 | 126 | require.True(t, result.IsNumber() && result.Uint32() == 256) 127 | <-B 128 | } 129 | 130 | func TestConcurrency(t *testing.T) { 131 | n := 32 132 | m := 10000 133 | 134 | var wg sync.WaitGroup 135 | wg.Add(n) 136 | 137 | req := make(chan struct{}, n) 138 | res := make(chan int64, m) 139 | 140 | for i := 0; i < n; i++ { 141 | go func() { 142 | stdruntime.LockOSThread() 143 | 144 | defer wg.Done() 145 | 146 | runtime := NewRuntime() 147 | defer runtime.Free() 148 | 149 | context := runtime.NewContext() 150 | defer context.Free() 151 | 152 | for range req { 153 | result, err := context.Eval(`new Date().getTime()`) 154 | require.NoError(t, err) 155 | 156 | res <- result.Int64() 157 | 158 | result.Free() 159 | } 160 | }() 161 | } 162 | 163 | for i := 0; i < m; i++ { 164 | req <- struct{}{} 165 | } 166 | close(req) 167 | 168 | wg.Wait() 169 | 170 | for i := 0; i < m; i++ { 171 | <-res 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /version.h: -------------------------------------------------------------------------------- 1 | #ifndef _GUARD_H_PORT_H_ 2 | #define _GUARD_H_PORT_H_ 3 | 4 | #define CONFIG_VERSION "2020-07-05" 5 | 6 | #endif 7 | --------------------------------------------------------------------------------