@@ -4126,12 +4126,35 @@ fn get_ascii_only_ident(sym: &str, may_need_quote: bool, target: EsVersion) -> C
4126
4126
}
4127
4127
4128
4128
fn get_quoted_utf16 ( v : & str , ascii_only : bool , target : EsVersion ) -> String {
4129
- let mut buf = String :: with_capacity ( v. len ( ) + 2 ) ;
4130
- let mut iter = v. chars ( ) . peekable ( ) ;
4129
+ // Count quotes first to determine which quote character to use
4130
+ let ( mut single_quote_count, mut double_quote_count) = ( 0 , 0 ) ;
4131
+ for c in v. chars ( ) {
4132
+ match c {
4133
+ '\'' => single_quote_count += 1 ,
4134
+ '"' => double_quote_count += 1 ,
4135
+ _ => { }
4136
+ }
4137
+ }
4138
+
4139
+ // Pre-calculate capacity to avoid reallocations
4140
+ let quote_char = if double_quote_count > single_quote_count {
4141
+ '\''
4142
+ } else {
4143
+ '"'
4144
+ } ;
4145
+ let escape_char = if quote_char == '\'' { '\'' } else { '"' } ;
4146
+ let escape_count = if quote_char == '\'' {
4147
+ single_quote_count
4148
+ } else {
4149
+ double_quote_count
4150
+ } ;
4131
4151
4132
- let mut single_quote_count = 0 ;
4133
- let mut double_quote_count = 0 ;
4152
+ // Add 2 for quotes, and 1 for each escaped quote
4153
+ let capacity = v. len ( ) + 2 + escape_count;
4154
+ let mut buf = String :: with_capacity ( capacity) ;
4155
+ buf. push ( quote_char) ;
4134
4156
4157
+ let mut iter = v. chars ( ) . peekable ( ) ;
4135
4158
while let Some ( c) = iter. next ( ) {
4136
4159
match c {
4137
4160
'\x00' => {
@@ -4149,29 +4172,24 @@ fn get_quoted_utf16(v: &str, ascii_only: bool, target: EsVersion) -> String {
4149
4172
'\t' => buf. push ( '\t' ) ,
4150
4173
'\\' => {
4151
4174
let next = iter. peek ( ) ;
4152
-
4153
4175
match next {
4154
- // TODO fix me - workaround for surrogate pairs
4155
4176
Some ( 'u' ) => {
4156
4177
let mut inner_iter = iter. clone ( ) ;
4157
-
4158
4178
inner_iter. next ( ) ;
4159
4179
4160
4180
let mut is_curly = false ;
4161
4181
let mut next = inner_iter. peek ( ) ;
4162
4182
4163
4183
if next == Some ( & '{' ) {
4164
4184
is_curly = true ;
4165
-
4166
4185
inner_iter. next ( ) ;
4167
4186
next = inner_iter. peek ( ) ;
4168
4187
} else if next != Some ( & 'D' ) && next != Some ( & 'd' ) {
4169
4188
buf. push ( '\\' ) ;
4170
4189
}
4171
4190
4172
4191
if let Some ( c @ 'D' | c @ 'd' ) = next {
4173
- let mut inner_buf = String :: new ( ) ;
4174
-
4192
+ let mut inner_buf = String :: with_capacity ( 8 ) ;
4175
4193
inner_buf. push ( '\\' ) ;
4176
4194
inner_buf. push ( 'u' ) ;
4177
4195
@@ -4180,21 +4198,17 @@ fn get_quoted_utf16(v: &str, ascii_only: bool, target: EsVersion) -> String {
4180
4198
}
4181
4199
4182
4200
inner_buf. push ( * c) ;
4183
-
4184
4201
inner_iter. next ( ) ;
4185
4202
4186
4203
let mut is_valid = true ;
4187
-
4188
4204
for _ in 0 ..3 {
4189
- let c = inner_iter. next ( ) ;
4190
-
4191
- match c {
4192
- Some ( '0' ..='9' ) | Some ( 'a' ..='f' ) | Some ( 'A' ..='F' ) => {
4193
- inner_buf. push ( c. unwrap ( ) ) ;
4205
+ match inner_iter. next ( ) {
4206
+ Some ( c @ '0' ..='9' ) | Some ( c @ 'a' ..='f' )
4207
+ | Some ( c @ 'A' ..='F' ) => {
4208
+ inner_buf. push ( c) ;
4194
4209
}
4195
4210
_ => {
4196
4211
is_valid = false ;
4197
-
4198
4212
break ;
4199
4213
}
4200
4214
}
@@ -4212,108 +4226,81 @@ fn get_quoted_utf16(v: &str, ascii_only: bool, target: EsVersion) -> String {
4212
4226
4213
4227
if is_valid {
4214
4228
let val_str = & inner_buf[ range] ;
4215
-
4216
- let v = u32:: from_str_radix ( val_str, 16 ) . unwrap_or_else ( |err| {
4217
- unreachable ! (
4218
- "failed to parse {} as a hex value: {:?}" ,
4219
- val_str, err
4220
- )
4221
- } ) ;
4222
-
4223
- if v > 0xffff {
4224
- buf. push_str ( & inner_buf) ;
4225
-
4226
- let end = if is_curly { 7 } else { 5 } ;
4227
-
4228
- for _ in 0 ..end {
4229
- iter. next ( ) ;
4229
+ if let Ok ( v) = u32:: from_str_radix ( val_str, 16 ) {
4230
+ if v > 0xffff {
4231
+ buf. push_str ( & inner_buf) ;
4232
+ let end = if is_curly { 7 } else { 5 } ;
4233
+ for _ in 0 ..end {
4234
+ iter. next ( ) ;
4235
+ }
4236
+ } else if ( 0xd800 ..=0xdfff ) . contains ( & v) {
4237
+ buf. push ( '\\' ) ;
4238
+ } else {
4239
+ buf. push_str ( "\\ \\ " ) ;
4230
4240
}
4231
- } else if ( 0xd800 ..=0xdfff ) . contains ( & v) {
4232
- buf. push ( '\\' ) ;
4233
4241
} else {
4234
4242
buf. push_str ( "\\ \\ " ) ;
4235
4243
}
4236
4244
} else {
4237
- buf. push_str ( "\\ \\ " )
4245
+ buf. push_str ( "\\ \\ " ) ;
4238
4246
}
4239
4247
} else if is_curly {
4240
4248
buf. push_str ( "\\ \\ " ) ;
4241
4249
} else {
4242
4250
buf. push ( '\\' ) ;
4243
4251
}
4244
4252
}
4245
- _ => {
4246
- buf. push_str ( "\\ \\ " ) ;
4247
- }
4253
+ _ => buf. push_str ( "\\ \\ " ) ,
4248
4254
}
4249
4255
}
4250
- '\'' => {
4251
- single_quote_count += 1 ;
4252
- buf. push ( '\'' ) ;
4253
- }
4254
- '"' => {
4255
- double_quote_count += 1 ;
4256
- buf. push ( '"' ) ;
4256
+ c if c == escape_char => {
4257
+ buf. push ( '\\' ) ;
4258
+ buf. push ( c) ;
4257
4259
}
4258
4260
'\x01' ..='\x0f' => {
4259
- let _ = write ! ( buf, "\\ x0{:x}" , c as u8 ) ;
4261
+ buf. push_str ( "\\ x0" ) ;
4262
+ write ! ( & mut buf, "{:x}" , c as u8 ) . unwrap ( ) ;
4260
4263
}
4261
4264
'\x10' ..='\x1f' => {
4262
- let _ = write ! ( buf, "\\ x{:x}" , c as u8 ) ;
4263
- }
4264
- '\x20' ..='\x7e' => {
4265
- buf. push ( c) ;
4265
+ buf. push_str ( "\\ x" ) ;
4266
+ write ! ( & mut buf, "{:x}" , c as u8 ) . unwrap ( ) ;
4266
4267
}
4268
+ '\x20' ..='\x7e' => buf. push ( c) ,
4267
4269
'\u{7f}' ..='\u{ff}' => {
4268
4270
if ascii_only || target <= EsVersion :: Es5 {
4269
- let _ = write ! ( buf, "\\ x{:x}" , c as u8 ) ;
4271
+ buf. push_str ( "\\ x" ) ;
4272
+ write ! ( & mut buf, "{:x}" , c as u8 ) . unwrap ( ) ;
4270
4273
} else {
4271
4274
buf. push ( c) ;
4272
4275
}
4273
4276
}
4274
- '\u{2028}' => {
4275
- buf. push_str ( "\\ u2028" ) ;
4276
- }
4277
- '\u{2029}' => {
4278
- buf. push_str ( "\\ u2029" ) ;
4279
- }
4280
- '\u{FEFF}' => {
4281
- buf. push_str ( "\\ uFEFF" ) ;
4282
- }
4283
- _ => {
4277
+ '\u{2028}' => buf. push_str ( "\\ u2028" ) ,
4278
+ '\u{2029}' => buf. push_str ( "\\ u2029" ) ,
4279
+ '\u{FEFF}' => buf. push_str ( "\\ uFEFF" ) ,
4280
+ c => {
4284
4281
if c. is_ascii ( ) {
4285
4282
buf. push ( c) ;
4286
4283
} else if c > '\u{FFFF}' {
4287
- // if we've got this far the char isn't reserved and if the callee has specified
4288
- // we should output unicode for non-ascii chars then we have
4289
- // to make sure we output unicode that is safe for the target
4290
- // Es5 does not support code point escapes and so surrograte formula must be
4291
- // used
4292
4284
if target <= EsVersion :: Es5 {
4293
- // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
4294
4285
let h = ( ( c as u32 - 0x10000 ) / 0x400 ) + 0xd800 ;
4295
4286
let l = ( c as u32 - 0x10000 ) % 0x400 + 0xdc00 ;
4296
-
4297
- let _ = write ! ( buf, "\\ u{:04X}\\ u{:04X}" , h, l) ;
4287
+ write ! ( & mut buf, "\\ u{:04X}\\ u{:04X}" , h, l) . unwrap ( ) ;
4298
4288
} else if ascii_only {
4299
- let _ = write ! ( buf, "\\ u{{{:04X}}}" , c as u32 ) ;
4289
+ write ! ( & mut buf, "\\ u{{{:04X}}}" , c as u32 ) . unwrap ( ) ;
4300
4290
} else {
4301
4291
buf. push ( c) ;
4302
4292
}
4303
4293
} else if ascii_only {
4304
- let _ = write ! ( buf, "\\ u{:04X}" , c as u16 ) ;
4294
+ write ! ( & mut buf, "\\ u{:04X}" , c as u16 ) . unwrap ( ) ;
4305
4295
} else {
4306
4296
buf. push ( c) ;
4307
4297
}
4308
4298
}
4309
4299
}
4310
4300
}
4311
4301
4312
- if double_quote_count > single_quote_count {
4313
- format ! ( "'{}'" , buf. replace( '\'' , "\\ '" ) )
4314
- } else {
4315
- format ! ( "\" {}\" " , buf. replace( '"' , "\\ \" " ) )
4316
- }
4302
+ buf. push ( quote_char) ;
4303
+ buf
4317
4304
}
4318
4305
4319
4306
fn handle_invalid_unicodes ( s : & str ) -> Cow < str > {
0 commit comments