@@ -2321,11 +2321,11 @@ JSContext *JS_NewContextRaw(JSRuntime *rt)
23212321JSContext *JS_NewContext(JSRuntime *rt)
23222322{
23232323 JSContext *ctx;
2324-
23252324 ctx = JS_NewContextRaw(rt);
23262325 if (!ctx)
23272326 return NULL;
23282327
2328+ JS_AddIntrinsicDOMException(ctx);
23292329 JS_AddIntrinsicBaseObjects(ctx);
23302330 JS_AddIntrinsicDate(ctx);
23312331 JS_AddIntrinsicEval(ctx);
@@ -2337,8 +2337,7 @@ JSContext *JS_NewContext(JSRuntime *rt)
23372337 JS_AddIntrinsicPromise(ctx);
23382338 JS_AddIntrinsicBigInt(ctx);
23392339 JS_AddIntrinsicWeakRef(ctx);
2340- JS_AddIntrinsicDOMException(ctx);
2341-
2340+ JS_AddIntrinsicBase64(ctx);
23422341 JS_AddPerformance(ctx);
23432342
23442343 return ctx;
@@ -4039,26 +4038,26 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
40394038 size_t len;
40404039 int kind;
40414040
4042- if (buf_len <= 0) {
4041+ if (unlikely( buf_len <= 0) ) {
40434042 return JS_AtomToString(ctx, JS_ATOM_empty_string);
40444043 }
40454044 /* Compute string kind and length: 7-bit, 8-bit, 16-bit, 16-bit UTF-16 */
40464045 kind = utf8_scan(buf, buf_len, &len);
4047- if (len > JS_STRING_LEN_MAX)
4046+ if (unlikely( len > JS_STRING_LEN_MAX) )
40484047 return JS_ThrowRangeError(ctx, "invalid string length");
40494048
40504049 switch (kind) {
40514050 case UTF8_PLAIN_ASCII:
40524051 str = js_alloc_string(ctx, len, 0);
4053- if (!str)
4052+ if (unlikely( !str) )
40544053 return JS_EXCEPTION;
40554054 memcpy(str8(str), buf, len);
40564055 str8(str)[len] = '\0';
40574056 break;
40584057 case UTF8_NON_ASCII:
40594058 /* buf contains non-ASCII code-points, but limited to 8-bit values */
40604059 str = js_alloc_string(ctx, len, 0);
4061- if (!str)
4060+ if (unlikely( !str) )
40624061 return JS_EXCEPTION;
40634062 utf8_decode_buf8(str8(str), len + 1, buf, buf_len);
40644063 break;
@@ -4067,7 +4066,7 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
40674066 //if (kind & UTF8_HAS_ERRORS)
40684067 // return JS_ThrowRangeError(ctx, "invalid UTF-8 sequence");
40694068 str = js_alloc_string(ctx, len, 1);
4070- if (!str)
4069+ if (unlikely( !str) )
40714070 return JS_EXCEPTION;
40724071 utf8_decode_buf16(str16(str), len, buf, buf_len);
40734072 break;
@@ -4079,10 +4078,10 @@ JSValue JS_NewTwoByteString(JSContext *ctx, const uint16_t *buf, size_t len)
40794078{
40804079 JSString *str;
40814080
4082- if (!len)
4081+ if (unlikely( !len) )
40834082 return JS_AtomToString(ctx, JS_ATOM_empty_string);
40844083 str = js_alloc_string(ctx, len, 1);
4085- if (!str)
4084+ if (unlikely( !str) )
40864085 return JS_EXCEPTION;
40874086 memcpy(str16(str), buf, len * sizeof(*buf));
40884087 return JS_MKPTR(JS_TAG_STRING, str);
@@ -57995,6 +57994,300 @@ void JS_AddIntrinsicDOMException(JSContext *ctx)
5799557994 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
5799657995 ctx->class_proto[JS_CLASS_DOM_EXCEPTION] = proto;
5799757996}
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 uint8_t 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 uint16_t B64_PAIR_LUT[4096];
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] = (uint16_t)(B64_ENC[a] | (B64_ENC[b] << 8));
58052+ }
58053+ B64_PAIR_INIT = 1;
58054+ }
58055+ }
58056+
58057+ static inline size_t b64_encode(const uint8_t *src, size_t len, char *dst) {
58058+ b64_pair_init_once();
58059+ size_t i=0, j=0;
58060+ size_t main = (len/3)*3;
58061+ for (; i<main; i+=3, j+=4) {
58062+ uint32_t v = (src[i]<<16)|(src[i+1]<<8)|src[i+2];
58063+ uint16_t p0 = B64_PAIR_LUT[(v>>12)&0xFFF];
58064+ uint16_t p1 = B64_PAIR_LUT[v & 0xFFF];
58065+ memcpy(dst+j, &p0, 2);
58066+ memcpy(dst+j+2, &p1, 2);
58067+ }
58068+ size_t rem = len-i;
58069+ if (unlikely(rem==1)) {
58070+ uint32_t v = src[i]<<16;
58071+ dst[j++] = B64_ENC[(v>>18)&63];
58072+ dst[j++] = B64_ENC[(v>>12)&63];
58073+ dst[j++] = '=';
58074+ dst[j++] = '=';
58075+ } else if (unlikely(rem==2)) {
58076+ uint32_t v = (src[i]<<16)|(src[i+1]<<8);
58077+ dst[j++] = B64_ENC[(v>>18)&63];
58078+ dst[j++] = B64_ENC[(v>>12)&63];
58079+ dst[j++] = B64_ENC[(v>>6)&63];
58080+ dst[j++] = '=';
58081+ }
58082+ return j;
58083+ }
58084+ static inline size_t
58085+ b64_decode(const char *src, size_t len, uint8_t *dst, int *err)
58086+ {
58087+ uint32_t acc = 0;
58088+ int bits = 0;
58089+ size_t j = 0;
58090+ int seen_pad = 0;
58091+
58092+ if (unlikely(err)) *err = 0;
58093+
58094+ for (size_t i=0; i<len; i++) {
58095+ unsigned ch = (unsigned char)src[i];
58096+ uint8_t flag = B64_FLAGS[ch];
58097+
58098+ if (likely(flag & K_VAL)) {
58099+ // normal sextet
58100+ if (unlikely(seen_pad)) { if (err) *err = 1; return 0; }
58101+ acc = (acc << 6) | B64_VAL[ch];
58102+ bits += 6;
58103+ if (bits >= 8) {
58104+ bits -= 8;
58105+ dst[j++] = (uint8_t)((acc >> bits) & 0xFF);
58106+ }
58107+ } else if (flag & K_WS) {
58108+ // whitespace -> skip
58109+ continue;
58110+ } else if (flag & K_PAD) {
58111+ // '=' padding
58112+ seen_pad = 1;
58113+ // After '=', only ws or '=' is valid
58114+ // Validate remaining input
58115+ for (size_t k=i+1; k<len; k++) {
58116+ unsigned ch2 = (unsigned char)src[k];
58117+ uint8_t f2 = B64_FLAGS[ch2];
58118+ if (f2 & K_WS) continue; // ws
58119+ if (ch2!='=') { if (err) *err = 1; return 0; }
58120+ }
58121+ break;
58122+ } else {
58123+ // invalid
58124+ if (err) *err = 1;
58125+ return 0;
58126+ }
58127+ }
58128+
58129+ // Leftover bits are only valid if 0–2 '=' pads handled it
58130+ if (unlikely(bits >= 6)) {
58131+ if (err) *err = 1;
58132+ return 0;
58133+ }
58134+
58135+ return j;
58136+ }
58137+
58138+ static JSValue js_btoa(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
58139+ {
58140+ JSValue val = likely(JS_IsString(argv[0])) ? JS_DupValue(ctx, argv[0])
58141+ : JS_ToString(ctx, argv[0]);
58142+ if (unlikely(JS_IsException(val)))
58143+ return JS_EXCEPTION;
58144+
58145+ JSString *s = JS_VALUE_GET_STRING(val);
58146+ size_t len = (size_t)s->len;
58147+
58148+ const uint8_t *in8 = NULL;
58149+ uint8_t *tmp = NULL;
58150+
58151+ if (likely(!s->is_wide_char)) {
58152+ in8 = (const uint8_t *)str8(s);
58153+ } else {
58154+ const uint16_t *src = str16(s);
58155+ tmp = js_malloc(ctx, len ? len : 1);
58156+ if (unlikely(!tmp)) {
58157+ JS_FreeValue(ctx, val);
58158+ return JS_ThrowOutOfMemory(ctx);
58159+ }
58160+ for (size_t i = 0; i < len; i++) {
58161+ uint32_t c = src[i];
58162+ if (unlikely(c > 0xFF)) {
58163+ js_free(ctx, tmp);
58164+ JS_FreeValue(ctx, val);
58165+ return JS_ThrowDOMException(ctx, "InvalidCharacterError",
58166+ "String contains an invalid character");
58167+ }
58168+ tmp[i] = (uint8_t)c;
58169+ }
58170+ in8 = tmp;
58171+ }
58172+
58173+ if (unlikely(len > (SIZE_MAX - 2) / 3)) {
58174+ if (tmp) js_free(ctx, tmp);
58175+ JS_FreeValue(ctx, val);
58176+ return JS_ThrowRangeError(ctx, "input too large");
58177+ }
58178+ size_t out_len = 4 * ((len + 2) / 3);
58179+ if (unlikely(out_len > JS_STRING_LEN_MAX)) {
58180+ if (tmp) js_free(ctx, tmp);
58181+ JS_FreeValue(ctx, val);
58182+ return JS_ThrowRangeError(ctx, "output too large");
58183+ }
58184+
58185+ JSString *ostr = js_alloc_string(ctx, out_len, /*is_wide=*/0);
58186+ if (unlikely(!ostr)) {
58187+ if (tmp) js_free(ctx, tmp);
58188+ JS_FreeValue(ctx, val);
58189+ return JS_EXCEPTION;
58190+ }
58191+
58192+ b64_encode(in8, len, (char *)str8(ostr));
58193+ ostr->len = out_len; // ensure length matches
58194+
58195+ if (tmp) js_free(ctx, tmp);
58196+ JS_FreeValue(ctx, val);
58197+ return JS_MKPTR(JS_TAG_STRING, ostr);
58198+ }
58199+
58200+ static JSValue js_atob(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
58201+ {
58202+ JSValue val = likely(JS_IsString(argv[0])) ? JS_DupValue(ctx, argv[0])
58203+ : JS_ToString(ctx, argv[0]);
58204+ if (unlikely(JS_IsException(val)))
58205+ return JS_EXCEPTION;
58206+
58207+ JSString *s = JS_VALUE_GET_STRING(val);
58208+ size_t slen = (size_t)s->len;
58209+
58210+ const uint8_t *in;
58211+ uint8_t *tmp = NULL;
58212+
58213+ if (likely(!s->is_wide_char)) {
58214+ const uint8_t *p = (const uint8_t *)str8(s);
58215+ // validate ASCII fast path
58216+ for (size_t i = 0; i < slen; i++) {
58217+ if (unlikely(p[i] & 0x80)) {
58218+ JS_FreeValue(ctx, val);
58219+ return JS_ThrowDOMException(ctx, "InvalidCharacterError",
58220+ "The string to be decoded is not correctly encoded");
58221+ }
58222+ }
58223+ in = p;
58224+ } else {
58225+ const uint16_t *src = str16(s);
58226+ tmp = js_malloc(ctx, slen ? slen : 1);
58227+ if (unlikely(!tmp)) {
58228+ JS_FreeValue(ctx, val);
58229+ return JS_ThrowOutOfMemory(ctx);
58230+ }
58231+ for (size_t i = 0; i < slen; i++) {
58232+ if (unlikely(src[i] > 0x7F)) {
58233+ js_free(ctx, tmp);
58234+ JS_FreeValue(ctx, val);
58235+ return JS_ThrowDOMException(ctx, "InvalidCharacterError",
58236+ "The string to be decoded is not correctly encoded");
58237+ }
58238+ tmp[i] = (uint8_t)src[i];
58239+ }
58240+ in = tmp;
58241+ }
58242+
58243+ // Max decoded size: slen*3/4
58244+ if (unlikely(slen > (SIZE_MAX / 3) * 4)) {
58245+ if (tmp) js_free(ctx, tmp);
58246+ JS_FreeValue(ctx, val);
58247+ return JS_ThrowRangeError(ctx, "input too large");
58248+ }
58249+ size_t out_cap = (slen / 4) * 3 + 3; // safe bound
58250+ if (unlikely(out_cap > JS_STRING_LEN_MAX)) {
58251+ if (tmp) js_free(ctx, tmp);
58252+ JS_FreeValue(ctx, val);
58253+ return JS_ThrowRangeError(ctx, "output too large");
58254+ }
58255+
58256+ JSString *ostr = js_alloc_string(ctx, out_cap, /*is_wide=*/0);
58257+ if (unlikely(!ostr)) {
58258+ if (tmp) js_free(ctx, tmp);
58259+ JS_FreeValue(ctx, val);
58260+ return JS_EXCEPTION;
58261+ }
58262+
58263+ uint8_t *dst = (uint8_t *)str8(ostr);
58264+ int err = 0;
58265+ size_t out_len = b64_decode((const char *)in, slen, dst, &err);
58266+
58267+ if (unlikely(tmp)) js_free(ctx, tmp);
58268+ JS_FreeValue(ctx, val);
58269+
58270+ if (unlikely(err)) {
58271+ js_free(ctx, ostr);
58272+ return JS_ThrowDOMException(ctx, "InvalidCharacterError",
58273+ "The string to be decoded is not correctly encoded");
58274+
58275+ }
58276+
58277+ ostr->len = out_len;
58278+ return JS_MKPTR(JS_TAG_STRING, ostr);
58279+ }
58280+
58281+ static const JSCFunctionListEntry js_base64_funcs[] = {
58282+ JS_CFUNC_DEF("btoa", 1, js_btoa),
58283+ JS_CFUNC_DEF("atob", 1, js_atob)};
58284+
58285+ void JS_AddIntrinsicBase64(JSContext *ctx)
58286+ {
58287+ JSValue global = JS_GetGlobalObject(ctx);
58288+ JS_SetPropertyFunctionList(ctx, global, js_base64_funcs, sizeof(js_base64_funcs) / sizeof(js_base64_funcs[0]));
58289+ JS_FreeValue(ctx, global);
58290+ }
5799858291
5799958292bool JS_DetectModule(const char *input, size_t input_len)
5800058293{
0 commit comments