Skip to content

Commit

Permalink
refactor(codegen)!: Codegen::into_source_text consume Codegen (#6539
Browse files Browse the repository at this point in the history
)

Breaking change. `Codegen::into_source_text` consume `Codegen`, instead of taking the `CodeBuffer` and substituting an empty one.

Keeping the `Codegen` alive seems unintuitive, as its state is then out of sync. Consuming the `CodeBuffer` is also marginally cheaper.
  • Loading branch information
overlookmotel committed Oct 14, 2024
1 parent 7e909a7 commit c0e9d7e
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 42 deletions.
53 changes: 16 additions & 37 deletions crates/oxc_codegen/src/code_buffer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::mem;

use assert_unchecked::assert_unchecked;

/// A string builder for constructing source code.
Expand All @@ -8,8 +6,7 @@ use assert_unchecked::assert_unchecked;
/// Essentially same as `String` but with additional methods.
///
/// Use one of the various `print_*` methods to add text into the buffer.
/// When you are done, call [`take_source_text`] or `String::from(code_buffer)`
/// to extract the final [`String`].
/// When you are done, call [`into_string`] to extract the final [`String`].
///
/// # Example
/// ```
Expand All @@ -26,10 +23,10 @@ use assert_unchecked::assert_unchecked;
/// code.print_str(" console.log('Hello, world!');\n");
/// code.print_str("}\n");
///
/// let source = code.take_source_text();
/// let source = code.into_string();
/// ```
///
/// [`take_source_text`]: CodeBuffer::take_source_text
/// [`into_string`]: CodeBuffer::into_string
#[derive(Debug, Default, Clone)]
pub struct CodeBuffer {
/// INVARIANT: `buf` is a valid UTF-8 string.
Expand All @@ -46,7 +43,7 @@ impl CodeBuffer {
///
/// // use `code` to build new source text
/// code.print_str("fn main() { println!(\"Hello, world!\"); }");
/// let source_text = code.take_source_text();
/// let source_text = code.into_string();
/// ```
#[inline]
pub fn new() -> Self {
Expand Down Expand Up @@ -187,7 +184,7 @@ impl CodeBuffer {
/// code.print_ascii_byte(b'o');
/// code.print_ascii_byte(b'o');
///
/// let source = code.take_source_text();
/// let source = code.into_string();
/// assert_eq!(source, "foo");
/// ```
#[inline]
Expand All @@ -212,7 +209,7 @@ impl CodeBuffer {
///
/// It is safe for a single call to temporarily result in invalid UTF-8, as long as
/// UTF-8 integrity is restored before calls to any other `print_*` method or
/// [`take_source_text`]. This lets you, for example, print an 4-byte Unicode character
/// [`into_string`]. This lets you, for example, print an 4-byte Unicode character
/// using 4 separate calls to this method. However, consider using [`print_bytes_unchecked`]
/// instead for that use case.
///
Expand All @@ -237,7 +234,7 @@ impl CodeBuffer {
///
/// [`print_ascii_byte`]: CodeBuffer::print_ascii_byte
/// [`print_char`]: CodeBuffer::print_char
/// [`take_source_text`]: CodeBuffer::take_source_text
/// [`into_string`]: CodeBuffer::into_string
/// [`print_bytes_unchecked`]: CodeBuffer::print_bytes_unchecked
#[inline]
pub unsafe fn print_byte_unchecked(&mut self, byte: u8) {
Expand Down Expand Up @@ -382,31 +379,25 @@ impl CodeBuffer {
&self.buf
}

/// Convert a buffer into a string of source code, leaving its internal buffer empty.
///
/// It is safe to re-use a `CodeBuffer` after calling this method, but there is little benefit
/// to doing so, as the `CodeBuffer` will be left in an empty state with no backing allocation.
///
/// You may alternatively use `String::from(code_buffer)`, which may be slightly more efficient.
/// Consume buffer and return source code as a `String`.
///
/// # Example
/// ```
/// use oxc_codegen::CodeBuffer;
/// let mut code = CodeBuffer::new();
/// code.print_str("console.log('foo');");
///
/// let source = code.take_source_text();
/// let source = code.into_string();
/// assert_eq!(source, "console.log('foo');");
/// assert!(code.is_empty());
/// ```
#[must_use]
#[inline]
pub fn take_source_text(&mut self) -> String {
pub fn into_string(self) -> String {
if cfg!(debug_assertions) {
String::from_utf8(mem::take(&mut self.buf)).unwrap()
String::from_utf8(self.buf).unwrap()
} else {
// SAFETY: All methods of `CodeBuffer` ensure `buf` is valid UTF-8
unsafe { String::from_utf8_unchecked(mem::take(&mut self.buf)) }
unsafe { String::from_utf8_unchecked(self.buf) }
}
}
}
Expand All @@ -420,13 +411,8 @@ impl AsRef<[u8]> for CodeBuffer {

impl From<CodeBuffer> for String {
#[inline]
fn from(buffer: CodeBuffer) -> Self {
if cfg!(debug_assertions) {
String::from_utf8(buffer.buf).unwrap()
} else {
// SAFETY: All methods of `CodeBuffer` ensure `buf` is valid UTF-8
unsafe { String::from_utf8_unchecked(buffer.buf) }
}
fn from(code: CodeBuffer) -> Self {
code.into_string()
}
}

Expand All @@ -452,20 +438,13 @@ mod test {
}

#[test]
fn into_source_string() {
fn into_string() {
let s = "Hello, world!";
let mut code = CodeBuffer::with_capacity(s.len());
code.print_str(s);

let source = code.take_source_text();
let source = code.into_string();
assert_eq!(source, s);

// buffer has been emptied
assert!(code.is_empty());
assert_eq!(code.len(), 0);
let empty_slice: &[u8] = &[];
assert_eq!(code.as_bytes(), empty_slice);
assert_eq!(String::from(code), "");
}

#[test]
Expand Down
10 changes: 5 additions & 5 deletions crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,13 @@ impl<'a> Default for Codegen<'a> {
}

impl<'a> From<Codegen<'a>> for String {
fn from(mut val: Codegen<'a>) -> Self {
fn from(val: Codegen<'a>) -> Self {
val.into_source_text()
}
}

impl<'a> From<Codegen<'a>> for Cow<'a, str> {
fn from(mut val: Codegen<'a>) -> Self {
fn from(val: Codegen<'a>) -> Self {
Cow::Owned(val.into_source_text())
}
}
Expand Down Expand Up @@ -215,14 +215,14 @@ impl<'a> Codegen<'a> {
}

program.print(&mut self, Context::default());
let code = self.into_source_text();
let code = self.code.into_string();
let map = self.sourcemap_builder.map(SourcemapBuilder::into_sourcemap);
CodegenReturn { code, map }
}

#[must_use]
pub fn into_source_text(&mut self) -> String {
self.code.take_source_text()
pub fn into_source_text(self) -> String {
self.code.into_string()
}

/// Push a single ASCII byte into the buffer.
Expand Down

0 comments on commit c0e9d7e

Please sign in to comment.