@@ -2321,11 +2321,11 @@ JSContext *JS_NewContextRaw(JSRuntime *rt)
2321
2321
JSContext *JS_NewContext(JSRuntime *rt)
2322
2322
{
2323
2323
JSContext *ctx;
2324
-
2325
2324
ctx = JS_NewContextRaw(rt);
2326
2325
if (!ctx)
2327
2326
return NULL;
2328
2327
2328
+ JS_AddIntrinsicDOMException(ctx);
2329
2329
JS_AddIntrinsicBaseObjects(ctx);
2330
2330
JS_AddIntrinsicDate(ctx);
2331
2331
JS_AddIntrinsicEval(ctx);
@@ -2337,8 +2337,7 @@ JSContext *JS_NewContext(JSRuntime *rt)
2337
2337
JS_AddIntrinsicPromise(ctx);
2338
2338
JS_AddIntrinsicBigInt(ctx);
2339
2339
JS_AddIntrinsicWeakRef(ctx);
2340
- JS_AddIntrinsicDOMException(ctx);
2341
-
2340
+ JS_AddIntrinsicBase64(ctx);
2342
2341
JS_AddPerformance(ctx);
2343
2342
2344
2343
return ctx;
@@ -4039,26 +4038,26 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
4039
4038
size_t len;
4040
4039
int kind;
4041
4040
4042
- if (buf_len <= 0) {
4041
+ if (unlikely( buf_len <= 0) ) {
4043
4042
return JS_AtomToString(ctx, JS_ATOM_empty_string);
4044
4043
}
4045
4044
/* Compute string kind and length: 7-bit, 8-bit, 16-bit, 16-bit UTF-16 */
4046
4045
kind = utf8_scan(buf, buf_len, &len);
4047
- if (len > JS_STRING_LEN_MAX)
4046
+ if (unlikely( len > JS_STRING_LEN_MAX) )
4048
4047
return JS_ThrowRangeError(ctx, "invalid string length");
4049
4048
4050
4049
switch (kind) {
4051
4050
case UTF8_PLAIN_ASCII:
4052
4051
str = js_alloc_string(ctx, len, 0);
4053
- if (!str)
4052
+ if (unlikely( !str) )
4054
4053
return JS_EXCEPTION;
4055
4054
memcpy(str8(str), buf, len);
4056
4055
str8(str)[len] = '\0';
4057
4056
break;
4058
4057
case UTF8_NON_ASCII:
4059
4058
/* buf contains non-ASCII code-points, but limited to 8-bit values */
4060
4059
str = js_alloc_string(ctx, len, 0);
4061
- if (!str)
4060
+ if (unlikely( !str) )
4062
4061
return JS_EXCEPTION;
4063
4062
utf8_decode_buf8(str8(str), len + 1, buf, buf_len);
4064
4063
break;
@@ -4067,7 +4066,7 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
4067
4066
//if (kind & UTF8_HAS_ERRORS)
4068
4067
// return JS_ThrowRangeError(ctx, "invalid UTF-8 sequence");
4069
4068
str = js_alloc_string(ctx, len, 1);
4070
- if (!str)
4069
+ if (unlikely( !str) )
4071
4070
return JS_EXCEPTION;
4072
4071
utf8_decode_buf16(str16(str), len, buf, buf_len);
4073
4072
break;
@@ -4079,10 +4078,10 @@ JSValue JS_NewTwoByteString(JSContext *ctx, const uint16_t *buf, size_t len)
4079
4078
{
4080
4079
JSString *str;
4081
4080
4082
- if (!len)
4081
+ if (unlikely( !len) )
4083
4082
return JS_AtomToString(ctx, JS_ATOM_empty_string);
4084
4083
str = js_alloc_string(ctx, len, 1);
4085
- if (!str)
4084
+ if (unlikely( !str) )
4086
4085
return JS_EXCEPTION;
4087
4086
memcpy(str16(str), buf, len * sizeof(*buf));
4088
4087
return JS_MKPTR(JS_TAG_STRING, str);
@@ -57995,6 +57994,308 @@ void JS_AddIntrinsicDOMException(JSContext *ctx)
57995
57994
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
57996
57995
ctx->class_proto[JS_CLASS_DOM_EXCEPTION] = proto;
57997
57996
}
57997
+ /* base64 */
57998
+
57999
+ static const unsigned char B64_ENC[64] = {
58000
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
58001
+ 'Q','R','S','T','U','V','W','X','Y','Z',
58002
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
58003
+ 'q','r','s','t','u','v','w','x','y','z',
58004
+ '0','1','2','3','4','5','6','7','8','9',
58005
+ '-','_'
58006
+ };
58007
+
58008
+ // Bit flags (0 means "invalid")
58009
+ enum { K_VAL = 1u, K_WS = 2u, K_PAD = 4u };
58010
+
58011
+ // sextet values for valid chars (others don't matter)
58012
+ static const uint8_t B64_VAL[256] = {
58013
+ ['A']=0, ['B']=1, ['C']=2, ['D']=3, ['E']=4, ['F']=5, ['G']=6, ['H']=7,
58014
+ ['I']=8, ['J']=9, ['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15,
58015
+ ['Q']=16,['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23,['Y']=24,['Z']=25,
58016
+ ['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31,['g']=32,['h']=33,
58017
+ ['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39,['o']=40,['p']=41,
58018
+ ['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47,['w']=48,['x']=49,['y']=50,['z']=51,
58019
+ ['0']=52,['1']=53,['2']=54,['3']=55,['4']=56,['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,
58020
+ ['-']=62, ['_']=63, // base64url; swap to '+'/'/' if using standard base64
58021
+ };
58022
+
58023
+ // flag bitset: only non-zero entries listed; unspecified default to 0 (invalid)
58024
+ static const char B64_FLAGS[256] = {
58025
+ // whitespace
58026
+ [' ']=K_WS, ['\t']=K_WS, ['\r']=K_WS, ['\n']=K_WS,
58027
+ // padding
58028
+ ['=']=K_PAD,
58029
+ // valid chars
58030
+ ['A']=K_VAL,['B']=K_VAL,['C']=K_VAL,['D']=K_VAL,['E']=K_VAL,['F']=K_VAL,['G']=K_VAL,['H']=K_VAL,
58031
+ ['I']=K_VAL,['J']=K_VAL,['K']=K_VAL,['L']=K_VAL,['M']=K_VAL,['N']=K_VAL,['O']=K_VAL,['P']=K_VAL,
58032
+ ['Q']=K_VAL,['R']=K_VAL,['S']=K_VAL,['T']=K_VAL,['U']=K_VAL,['V']=K_VAL,['W']=K_VAL,['X']=K_VAL,
58033
+ ['Y']=K_VAL,['Z']=K_VAL,
58034
+ ['a']=K_VAL,['b']=K_VAL,['c']=K_VAL,['d']=K_VAL,['e']=K_VAL,['f']=K_VAL,['g']=K_VAL,['h']=K_VAL,
58035
+ ['i']=K_VAL,['j']=K_VAL,['k']=K_VAL,['l']=K_VAL,['m']=K_VAL,['n']=K_VAL,['o']=K_VAL,['p']=K_VAL,
58036
+ ['q']=K_VAL,['r']=K_VAL,['s']=K_VAL,['t']=K_VAL,['u']=K_VAL,['v']=K_VAL,['w']=K_VAL,['x']=K_VAL,
58037
+ ['y']=K_VAL,['z']=K_VAL,
58038
+ ['0']=K_VAL,['1']=K_VAL,['2']=K_VAL,['3']=K_VAL,['4']=K_VAL,['5']=K_VAL,['6']=K_VAL,['7']=K_VAL,
58039
+ ['8']=K_VAL,['9']=K_VAL,
58040
+ ['-']=K_VAL,['_']=K_VAL
58041
+ };
58042
+
58043
+ static char B64_PAIR_LUT[4096][2];
58044
+ static int B64_PAIR_INIT = 0;
58045
+
58046
+ static inline void b64_pair_init_once(void) {
58047
+ if (unlikely(!B64_PAIR_INIT)) {
58048
+ for (int i = 0; i < 4096; i++) {
58049
+ uint8_t a = (uint8_t)(i >> 6);
58050
+ uint8_t b = (uint8_t)(i & 63);
58051
+ B64_PAIR_LUT[i][0] = B64_ENC[a];
58052
+ B64_PAIR_LUT[i][1] = B64_ENC[b];
58053
+ }
58054
+ B64_PAIR_INIT = 1;
58055
+ }
58056
+ }
58057
+
58058
+ static inline size_t b64_encode(const uint8_t *src, size_t len, char *dst) {
58059
+ b64_pair_init_once();
58060
+ size_t i = 0, j = 0;
58061
+ size_t main = (len/3)*3;
58062
+
58063
+ for (; i < main; i += 3, j += 4) {
58064
+ uint32_t v = ((uint32_t)src[i] << 16) | ((uint32_t)src[i+1] << 8) | (uint32_t)src[i+2];
58065
+ const char *p0 = B64_PAIR_LUT[(v >> 12) & 0xFFF]; // [ sextet0 | sextet1 ]
58066
+ const char *p1 = B64_PAIR_LUT[v & 0xFFF]; // [ sextet2 | sextet3 ]
58067
+ dst[j+0] = p0[0];
58068
+ dst[j+1] = p0[1];
58069
+ dst[j+2] = p1[0];
58070
+ dst[j+3] = p1[1];
58071
+ }
58072
+
58073
+ size_t rem = len - i;
58074
+ if (rem == 1) {
58075
+ uint32_t v = ((uint32_t)src[i] << 16);
58076
+ dst[j++] = B64_ENC[(v >> 18) & 63];
58077
+ dst[j++] = B64_ENC[(v >> 12) & 63];
58078
+ dst[j++] = '=';
58079
+ dst[j++] = '=';
58080
+ } else if (rem == 2) {
58081
+ uint32_t v = ((uint32_t)src[i] << 16) | ((uint32_t)src[i+1] << 8);
58082
+ dst[j++] = B64_ENC[(v >> 18) & 63];
58083
+ dst[j++] = B64_ENC[(v >> 12) & 63];
58084
+ dst[j++] = B64_ENC[(v >> 6) & 63];
58085
+ dst[j++] = '=';
58086
+ }
58087
+ return j;
58088
+ }
58089
+
58090
+ static inline size_t
58091
+ b64_decode(const char *src, size_t len, uint8_t *dst, int *err)
58092
+ {
58093
+ uint32_t acc = 0;
58094
+ int bits = 0;
58095
+ size_t j = 0;
58096
+ int seen_pad = 0;
58097
+
58098
+ if (unlikely(err)) *err = 0;
58099
+
58100
+ for (size_t i=0; i<len; i++) {
58101
+ unsigned ch = (unsigned char)src[i];
58102
+ uint8_t flag = B64_FLAGS[ch];
58103
+
58104
+ if (likely(flag & K_VAL)) {
58105
+ // normal sextet
58106
+ if (unlikely(seen_pad)) { if (err) *err = 1; return 0; }
58107
+ acc = (acc << 6) | B64_VAL[ch];
58108
+ bits += 6;
58109
+ if (bits >= 8) {
58110
+ bits -= 8;
58111
+ dst[j++] = (uint8_t)((acc >> bits) & 0xFF);
58112
+ }
58113
+ } else if (flag & K_WS) {
58114
+ // whitespace -> skip
58115
+ continue;
58116
+ } else if (flag & K_PAD) {
58117
+ // '=' padding
58118
+ seen_pad = 1;
58119
+ // After '=', only ws or '=' is valid
58120
+ // Validate remaining input
58121
+ for (size_t k=i+1; k<len; k++) {
58122
+ unsigned ch2 = (unsigned char)src[k];
58123
+ uint8_t f2 = B64_FLAGS[ch2];
58124
+ if (f2 & K_WS) continue; // ws
58125
+ if (ch2!='=') { if (err) *err = 1; return 0; }
58126
+ }
58127
+ break;
58128
+ } else {
58129
+ // invalid
58130
+ if (err) *err = 1;
58131
+ return 0;
58132
+ }
58133
+ }
58134
+
58135
+ // Leftover bits are only valid if 0–2 '=' pads handled it
58136
+ if (unlikely(bits >= 6)) {
58137
+ if (err) *err = 1;
58138
+ return 0;
58139
+ }
58140
+
58141
+ return j;
58142
+ }
58143
+
58144
+ static JSValue js_btoa(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
58145
+ {
58146
+ JSValue val = likely(JS_IsString(argv[0])) ? JS_DupValue(ctx, argv[0])
58147
+ : JS_ToString(ctx, argv[0]);
58148
+ if (unlikely(JS_IsException(val)))
58149
+ return JS_EXCEPTION;
58150
+
58151
+ JSString *s = JS_VALUE_GET_STRING(val);
58152
+ size_t len = (size_t)s->len;
58153
+
58154
+ const uint8_t *in8 = NULL;
58155
+ uint8_t *tmp = NULL;
58156
+
58157
+ if (likely(!s->is_wide_char)) {
58158
+ in8 = (const uint8_t *)str8(s);
58159
+ } else {
58160
+ const uint16_t *src = str16(s);
58161
+ tmp = js_malloc(ctx, likely(len) ? len : 1);
58162
+ if (unlikely(!tmp)) {
58163
+ JS_FreeValue(ctx, val);
58164
+ return JS_ThrowOutOfMemory(ctx);
58165
+ }
58166
+ for (size_t i = 0; i < len; i++) {
58167
+ uint32_t c = src[i];
58168
+ if (unlikely(c > 0xFF)) {
58169
+ js_free(ctx, tmp);
58170
+ JS_FreeValue(ctx, val);
58171
+ return JS_ThrowDOMException(ctx, "InvalidCharacterError",
58172
+ "String contains an invalid character");
58173
+ }
58174
+ tmp[i] = (uint8_t)c;
58175
+ }
58176
+ in8 = tmp;
58177
+ }
58178
+
58179
+ if (unlikely(len > (SIZE_MAX - 2) / 3)) {
58180
+ if (tmp) js_free(ctx, tmp);
58181
+ JS_FreeValue(ctx, val);
58182
+ return JS_ThrowRangeError(ctx, "input too large");
58183
+ }
58184
+ size_t out_len = 4 * ((len + 2) / 3);
58185
+ if (unlikely(out_len > JS_STRING_LEN_MAX)) {
58186
+ if (tmp) js_free(ctx, tmp);
58187
+ JS_FreeValue(ctx, val);
58188
+ return JS_ThrowRangeError(ctx, "output too large");
58189
+ }
58190
+
58191
+ JSString *ostr = js_alloc_string(ctx, out_len, /*is_wide=*/0);
58192
+ if (unlikely(!ostr)) {
58193
+ if (tmp) js_free(ctx, tmp);
58194
+ JS_FreeValue(ctx, val);
58195
+ return JS_EXCEPTION;
58196
+ }
58197
+ char *outp = (char *)str8(ostr);
58198
+
58199
+ size_t written = b64_encode(in8, len, outp);
58200
+ outp[written] = '\0';
58201
+ ostr->len = out_len; // ensure length matches
58202
+
58203
+ if (tmp) js_free(ctx, tmp);
58204
+ JS_FreeValue(ctx, val);
58205
+ return JS_MKPTR(JS_TAG_STRING, ostr);
58206
+ }
58207
+
58208
+ static JSValue js_atob(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
58209
+ {
58210
+ JSValue val = likely(JS_IsString(argv[0])) ? JS_DupValue(ctx, argv[0])
58211
+ : JS_ToString(ctx, argv[0]);
58212
+ if (unlikely(JS_IsException(val)))
58213
+ return JS_EXCEPTION;
58214
+
58215
+ JSString *s = JS_VALUE_GET_STRING(val);
58216
+ size_t slen = (size_t)s->len;
58217
+
58218
+ const uint8_t *in;
58219
+ uint8_t *tmp = NULL;
58220
+
58221
+ if (likely(!s->is_wide_char)) {
58222
+ const uint8_t *p = (const uint8_t *)str8(s);
58223
+ // validate ASCII fast path
58224
+ for (size_t i = 0; i < slen; i++) {
58225
+ if (unlikely(p[i] & 0x80)) {
58226
+ JS_FreeValue(ctx, val);
58227
+ return JS_ThrowDOMException(ctx, "InvalidCharacterError",
58228
+ "The string to be decoded is not correctly encoded");
58229
+ }
58230
+ }
58231
+ in = p;
58232
+ } else {
58233
+ const uint16_t *src = str16(s);
58234
+ tmp = js_malloc(ctx, likely(slen) ? slen : 1);
58235
+ if (unlikely(!tmp)) {
58236
+ JS_FreeValue(ctx, val);
58237
+ return JS_ThrowOutOfMemory(ctx);
58238
+ }
58239
+ for (size_t i = 0; i < slen; i++) {
58240
+ if (unlikely(src[i] > 0x7F)) {
58241
+ js_free(ctx, tmp);
58242
+ JS_FreeValue(ctx, val);
58243
+ return JS_ThrowDOMException(ctx, "InvalidCharacterError",
58244
+ "The string to be decoded is not correctly encoded");
58245
+ }
58246
+ tmp[i] = (uint8_t)src[i];
58247
+ }
58248
+ in = tmp;
58249
+ }
58250
+
58251
+ // Max decoded size: slen*3/4
58252
+ if (unlikely(slen > (SIZE_MAX / 3) * 4)) {
58253
+ if (tmp) js_free(ctx, tmp);
58254
+ JS_FreeValue(ctx, val);
58255
+ return JS_ThrowRangeError(ctx, "input too large");
58256
+ }
58257
+ size_t out_cap = (slen / 4) * 3 + 3; // safe bound
58258
+ if (unlikely(out_cap > JS_STRING_LEN_MAX)) {
58259
+ if (tmp) js_free(ctx, tmp);
58260
+ JS_FreeValue(ctx, val);
58261
+ return JS_ThrowRangeError(ctx, "output too large");
58262
+ }
58263
+
58264
+ JSString *ostr = js_alloc_string(ctx, out_cap, /*is_wide=*/0);
58265
+ if (unlikely(!ostr)) {
58266
+ if (tmp) js_free(ctx, tmp);
58267
+ JS_FreeValue(ctx, val);
58268
+ return JS_EXCEPTION;
58269
+ }
58270
+
58271
+ uint8_t *dst = (uint8_t *)str8(ostr);
58272
+ int err = 0;
58273
+ size_t out_len = b64_decode((const char *)in, slen, dst, &err);
58274
+
58275
+ if (unlikely(tmp)) js_free(ctx, tmp);
58276
+ JS_FreeValue(ctx, val);
58277
+
58278
+ if (unlikely(err)) {
58279
+ js_free_string(ctx->rt, ostr);
58280
+ return JS_ThrowDOMException(ctx, "InvalidCharacterError",
58281
+ "The string to be decoded is not correctly encoded");
58282
+
58283
+ }
58284
+ dst[out_len] = '\0';
58285
+ ostr->len = out_len;
58286
+ return JS_MKPTR(JS_TAG_STRING, ostr);
58287
+ }
58288
+
58289
+ static const JSCFunctionListEntry js_base64_funcs[] = {
58290
+ JS_CFUNC_DEF("btoa", 1, js_btoa),
58291
+ JS_CFUNC_DEF("atob", 1, js_atob)};
58292
+
58293
+ void JS_AddIntrinsicBase64(JSContext *ctx)
58294
+ {
58295
+ JSValue global = JS_GetGlobalObject(ctx);
58296
+ JS_SetPropertyFunctionList(ctx, global, js_base64_funcs, sizeof(js_base64_funcs) / sizeof(js_base64_funcs[0]));
58297
+ JS_FreeValue(ctx, global);
58298
+ }
57998
58299
57999
58300
bool JS_DetectModule(const char *input, size_t input_len)
58000
58301
{
0 commit comments