@@ -29308,6 +29308,23 @@ static __exception JSAtom js_parse_from_clause(JSParseState *s)
2930829308 return module_name;
2930929309}
2931029310
29311+ static bool has_unmatched_surrogate(const uint16_t *s, size_t n)
29312+ {
29313+ size_t i;
29314+
29315+ for (i = 0; i < n; i++) {
29316+ if (is_lo_surrogate(s[i]))
29317+ return true;
29318+ if (!is_hi_surrogate(s[i]))
29319+ continue;
29320+ if (++i == n)
29321+ return true;
29322+ if (!is_lo_surrogate(s[i]))
29323+ return true;
29324+ }
29325+ return false;
29326+ }
29327+
2931129328static __exception int js_parse_export(JSParseState *s)
2931229329{
2931329330 JSContext *ctx = s->ctx;
@@ -29340,23 +29357,40 @@ static __exception int js_parse_export(JSParseState *s)
2934029357 switch(tok) {
2934129358 case '{':
2934229359 first_export = m->export_entries_count;
29360+ bool has_string_binding = false;
2934329361 while (s->token.val != '}') {
29344- if (!token_is_ident(s->token.val)) {
29345- js_parse_error(s, "identifier expected");
29346- return -1;
29362+ if (token_is_ident(s->token.val)) {
29363+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
29364+ } else if (s->token.val == TOK_STRING) {
29365+ local_name = JS_ValueToAtom(ctx, s->token.u.str.str);
29366+ if (local_name == JS_ATOM_NULL)
29367+ return -1;
29368+ has_string_binding = true;
29369+ } else {
29370+ return js_parse_error(s, "identifier or string expected");
2934729371 }
29348- local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
2934929372 export_name = JS_ATOM_NULL;
2935029373 if (next_token(s))
2935129374 goto fail;
2935229375 if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
2935329376 if (next_token(s))
2935429377 goto fail;
29355- if (!token_is_ident(s->token.val)) {
29356- js_parse_error(s, "identifier expected");
29378+ if (token_is_ident(s->token.val)) {
29379+ export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
29380+ } else if (s->token.val == TOK_STRING) {
29381+ JSString *p = JS_VALUE_GET_STRING(s->token.u.str.str);
29382+ if (p->is_wide_char && has_unmatched_surrogate(str16(p), p->len)) {
29383+ js_parse_error(s, "illegal export name");
29384+ return -1;
29385+ }
29386+ export_name = JS_ValueToAtom(ctx, s->token.u.str.str);
29387+ if (export_name == JS_ATOM_NULL) {
29388+ return -1;
29389+ }
29390+ } else {
29391+ js_parse_error(s, "identifier or string expected");
2935729392 goto fail;
2935829393 }
29359- export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
2936029394 if (next_token(s)) {
2936129395 fail:
2936229396 JS_FreeAtom(ctx, local_name);
@@ -29393,18 +29427,26 @@ static __exception int js_parse_export(JSParseState *s)
2939329427 me->export_type = JS_EXPORT_TYPE_INDIRECT;
2939429428 me->u.req_module_idx = idx;
2939529429 }
29430+ } else if (has_string_binding) {
29431+ // Without 'from' clause, string literals cannot be used as local binding names
29432+ return js_parse_error(s, "string export name only allowed with 'from' clause");
2939629433 }
2939729434 break;
2939829435 case '*':
2939929436 if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
2940029437 /* export ns from */
2940129438 if (next_token(s))
2940229439 return -1;
29403- if (!token_is_ident(s->token.val)) {
29404- js_parse_error(s, "identifier expected");
29405- return -1;
29440+ if (token_is_ident(s->token.val)) {
29441+ export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
29442+ } else if (s->token.val == TOK_STRING) {
29443+ export_name = JS_ValueToAtom(ctx, s->token.u.str.str);
29444+ if (export_name == JS_ATOM_NULL) {
29445+ return -1;
29446+ }
29447+ } else {
29448+ return js_parse_error(s, "identifier or string expected");
2940629449 }
29407- export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
2940829450 if (next_token(s))
2940929451 goto fail1;
2941029452 module_name = js_parse_from_clause(s);
@@ -29578,11 +29620,15 @@ static __exception int js_parse_import(JSParseState *s)
2957829620 return -1;
2957929621
2958029622 while (s->token.val != '}') {
29581- if (!token_is_ident(s->token.val)) {
29582- js_parse_error(s, "identifier expected");
29583- return -1;
29623+ if (token_is_ident(s->token.val)) {
29624+ import_name = JS_DupAtom(ctx, s->token.u.ident.atom);
29625+ } else if (s->token.val == TOK_STRING) {
29626+ import_name = JS_ValueToAtom(ctx, s->token.u.str.str);
29627+ if (import_name == JS_ATOM_NULL)
29628+ return -1;
29629+ } else {
29630+ return js_parse_error(s, "identifier or string expected expected");
2958429631 }
29585- import_name = JS_DupAtom(ctx, s->token.u.ident.atom);
2958629632 local_name = JS_ATOM_NULL;
2958729633 if (next_token(s))
2958829634 goto fail;
0 commit comments