use std::{alloc::Layout, mem::MaybeUninit, slice, str};
use oxc_allocator::{Allocator, ArenaBox, GetAllocator};
use oxc_span::{SPAN, Span};
use oxc_str::Str;
use oxc_syntax::{number::NumberBase, operator::UnaryOperator, scope::ScopeId};
use crate::ast::*;
use super::GetAstBuilder;
impl<'a> Expression<'a> {
#[inline]
pub fn new_number_0(builder: &impl GetAstBuilder<'a>) -> Self {
let builder = builder.builder();
Expression::new_numeric_literal(SPAN, 0.0, None, NumberBase::Decimal, builder)
}
#[inline]
pub fn new_void_0(span: Span, builder: &impl GetAstBuilder<'a>) -> Self {
let builder = builder.builder();
let argument = Expression::new_number_0(builder);
Expression::new_unary_expression(span, UnaryOperator::Void, argument, builder)
}
#[inline]
pub fn new_nan(span: Span, builder: &impl GetAstBuilder<'a>) -> Self {
let builder = builder.builder();
Expression::new_numeric_literal(span, f64::NAN, None, NumberBase::Decimal, builder)
}
}
impl<'a> Directive<'a> {
#[inline]
pub fn new_use_strict(builder: &impl GetAstBuilder<'a>) -> Self {
let builder = builder.builder();
let use_strict = Str::from("use strict");
Directive::new(
SPAN,
StringLiteral::new(SPAN, use_strict, None, builder),
use_strict,
builder,
)
}
}
impl<'a> FormalParameter<'a> {
#[inline]
pub fn new_plain(
span: Span,
pattern: BindingPattern<'a>,
builder: &impl GetAstBuilder<'a>,
) -> Self {
let builder = builder.builder();
FormalParameter::new(span, [], pattern, None, None, false, None, false, false, builder)
}
}
impl<'a> Function<'a> {
#[inline]
pub fn boxed_plain_with_scope_id(
r#type: FunctionType,
span: Span,
id: Option<BindingIdentifier<'a>>,
params: ArenaBox<'a, FormalParameters<'a>>,
body: ArenaBox<'a, FunctionBody<'a>>,
scope_id: ScopeId,
builder: &impl GetAstBuilder<'a>,
) -> ArenaBox<'a, Self> {
let builder = builder.builder();
Function::boxed_with_scope_id_and_pure_and_pife(
span,
r#type,
id,
false,
false,
false,
None,
None,
params,
None,
Some(body),
scope_id,
false,
false,
builder,
)
}
#[inline]
pub fn boxed_with_scope_id(
span: Span,
r#type: FunctionType,
id: Option<BindingIdentifier<'a>>,
generator: bool,
r#async: bool,
declare: bool,
type_parameters: Option<ArenaBox<'a, TSTypeParameterDeclaration<'a>>>,
this_param: Option<ArenaBox<'a, TSThisParameter<'a>>>,
params: ArenaBox<'a, FormalParameters<'a>>,
return_type: Option<ArenaBox<'a, TSTypeAnnotation<'a>>>,
body: Option<ArenaBox<'a, FunctionBody<'a>>>,
scope_id: ScopeId,
builder: &impl GetAstBuilder<'a>,
) -> ArenaBox<'a, Self> {
let builder = builder.builder();
Function::boxed_with_scope_id_and_pure_and_pife(
span,
r#type,
id,
generator,
r#async,
declare,
type_parameters,
this_param,
params,
return_type,
body,
scope_id,
false,
false,
builder,
)
}
}
impl<'a> TemplateElement<'a> {
#[inline]
pub fn new_escape_raw(
span: Span,
mut value: TemplateElementValue<'a>,
tail: bool,
builder: &impl GetAstBuilder<'a>,
) -> Self {
let builder = builder.builder();
value.raw = escape_template_element_raw(value.raw, builder.allocator());
TemplateElement::new(span, value, tail, builder)
}
#[inline]
pub fn new_escape_raw_with_lone_surrogates(
span: Span,
mut value: TemplateElementValue<'a>,
tail: bool,
lone_surrogates: bool,
builder: &impl GetAstBuilder<'a>,
) -> Self {
let builder = builder.builder();
value.raw = escape_template_element_raw(value.raw, builder.allocator());
TemplateElement::new_with_lone_surrogates(span, value, tail, lone_surrogates, builder)
}
}
fn escape_template_element_raw<'a>(raw: Str<'a>, allocator: &'a Allocator) -> Str<'a> {
let bytes = raw.as_bytes();
let mut extra_bytes = 0usize;
for i in 0..bytes.len() {
extra_bytes += match bytes[i] {
b'\\' | b'`' | b'\r' => 1,
b'$' if bytes.get(i + 1) == Some(&b'{') => 1,
_ => 0,
};
}
if extra_bytes == 0 {
return raw;
}
let len = bytes.len() + extra_bytes;
let layout = Layout::array::<u8>(len).unwrap();
let ptr = allocator.alloc_layout(layout);
let dest = unsafe { slice::from_raw_parts_mut(ptr.as_ptr().cast::<MaybeUninit<u8>>(), len) };
let mut j = 0;
for i in 0..bytes.len() {
unsafe {
match bytes[i] {
b'\\' => {
dest.get_unchecked_mut(j).write(b'\\');
dest.get_unchecked_mut(j + 1).write(b'\\');
j += 2;
}
b'`' => {
dest.get_unchecked_mut(j).write(b'\\');
dest.get_unchecked_mut(j + 1).write(b'`');
j += 2;
}
b'$' if bytes.get(i + 1) == Some(&b'{') => {
dest.get_unchecked_mut(j).write(b'\\');
dest.get_unchecked_mut(j + 1).write(b'$');
j += 2;
}
b'\r' => {
dest.get_unchecked_mut(j).write(b'\\');
dest.get_unchecked_mut(j + 1).write(b'r');
j += 2;
}
b => {
dest.get_unchecked_mut(j).write(b);
j += 1;
}
}
}
}
debug_assert_eq!(j, len);
let bytes = unsafe { slice::from_raw_parts(dest.as_ptr().cast::<u8>(), len) };
let escaped = unsafe { str::from_utf8_unchecked(bytes) };
Str::from(escaped)
}