use std::{alloc::Layout, mem::MaybeUninit, slice, str};
use oxc_allocator::{Allocator, ArenaBox, ArenaVec, GetAllocator, IntoIn};
use oxc_span::{SPAN, Span};
use oxc_str::Str;
use oxc_syntax::{number::NumberBase, operator::UnaryOperator, scope::ScopeId};
use crate::ast::*;
use super::{GetAstBuilder, NONE};
impl<'a> Expression<'a> {
#[inline]
pub fn new_number_0<B: GetAstBuilder<'a>>(builder: &B) -> Self {
Expression::new_numeric_literal(SPAN, 0.0, None, NumberBase::Decimal, builder)
}
#[inline]
pub fn new_void_0<B: GetAstBuilder<'a>>(span: Span, builder: &B) -> Self {
let argument = Expression::new_number_0(builder);
Expression::new_unary_expression(span, UnaryOperator::Void, argument, builder)
}
#[inline]
pub fn new_nan<B: GetAstBuilder<'a>>(span: Span, builder: &B) -> Self {
Expression::new_numeric_literal(span, f64::NAN, None, NumberBase::Decimal, builder)
}
}
impl<'a> Directive<'a> {
#[inline]
pub fn new_use_strict<B: GetAstBuilder<'a>>(builder: &B) -> Self {
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<B: GetAstBuilder<'a>>(
span: Span,
pattern: BindingPattern<'a>,
builder: &B,
) -> Self {
let decorators = ArenaVec::new_in(builder.builder());
FormalParameter::new(
span, decorators, pattern, NONE, NONE, false, None, false, false, builder,
)
}
}
impl<'a> Function<'a> {
#[inline]
pub fn boxed_plain_with_scope_id<B: GetAstBuilder<'a>>(
r#type: FunctionType,
span: Span,
id: Option<BindingIdentifier<'a>>,
params: FormalParameters<'a>,
body: FunctionBody<'a>,
scope_id: ScopeId,
builder: &B,
) -> ArenaBox<'a, Self> {
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<B: GetAstBuilder<'a>, T1, T2, T3, T4, T5>(
span: Span,
r#type: FunctionType,
id: Option<BindingIdentifier<'a>>,
generator: bool,
r#async: bool,
declare: bool,
type_parameters: T1,
this_param: T2,
params: T3,
return_type: T4,
body: T5,
scope_id: ScopeId,
builder: &B,
) -> ArenaBox<'a, Self>
where
T1: IntoIn<'a, Option<ArenaBox<'a, TSTypeParameterDeclaration<'a>>>>,
T2: IntoIn<'a, Option<ArenaBox<'a, TSThisParameter<'a>>>>,
T3: IntoIn<'a, ArenaBox<'a, FormalParameters<'a>>>,
T4: IntoIn<'a, Option<ArenaBox<'a, TSTypeAnnotation<'a>>>>,
T5: IntoIn<'a, Option<ArenaBox<'a, FunctionBody<'a>>>>,
{
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> ExportNamedDeclaration<'a> {
#[inline]
pub fn boxed_plain<B: GetAstBuilder<'a>>(
span: Span,
specifiers: ArenaVec<'a, ExportSpecifier<'a>>,
source: Option<StringLiteral<'a>>,
builder: &B,
) -> ArenaBox<'a, Self> {
ExportNamedDeclaration::boxed(
span,
None,
specifiers,
source,
ImportOrExportKind::Value,
NONE,
builder,
)
}
#[inline]
pub fn boxed_plain_declaration<B: GetAstBuilder<'a>>(
span: Span,
declaration: Declaration<'a>,
builder: &B,
) -> ArenaBox<'a, Self> {
let specifiers = ArenaVec::new_in(builder.builder());
ExportNamedDeclaration::boxed(
span,
Some(declaration),
specifiers,
None,
ImportOrExportKind::Value,
NONE,
builder,
)
}
}
impl<'a> TemplateElement<'a> {
#[inline]
pub fn new_escape_raw<B: GetAstBuilder<'a>>(
span: Span,
mut value: TemplateElementValue<'a>,
tail: bool,
builder: &B,
) -> Self {
value.raw = escape_template_element_raw(value.raw, builder.builder().allocator());
TemplateElement::new(span, value, tail, builder)
}
#[inline]
pub fn new_escape_raw_with_lone_surrogates<B: GetAstBuilder<'a>>(
span: Span,
mut value: TemplateElementValue<'a>,
tail: bool,
lone_surrogates: bool,
builder: &B,
) -> Self {
value.raw = escape_template_element_raw(value.raw, builder.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)
}