<commit_before>use {Action, InputTranslator, InputRebind, MouseTranslationData};
use input::Button;
use window::Size;
use std::convert::Into;
use std::default::Default;

/// Convenience object for constructing an InputMap.
pub struct RebindBuilder<A: Action> {
    input_remappings: Vec<(Button, A)>,
    mouse_data: MouseTranslationData
}

impl<A: Action> RebindBuilder<A> {
    /// Creates a new `RebindBuilder` with the specified viewport size.
    pub fn new(size: Size) -> Self {
        RebindBuilder {
            input_remappings: vec![],
            mouse_data: MouseTranslationData::new(size)
        }
    }

    /// Set whether the x scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn x_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_scroll_inverted = invert;
        self
    }

    /// Returns true if the x scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn get_x_scroll_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_scroll_inverted
    }

    /// Set whether the y scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn y_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_scroll_inverted = invert;
        self
    }

    /// Returns true if the y scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn get_y_scroll_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_scroll_inverted
    }

    /// Set whether the x axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn x_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_motion_inverted = invert;
        self
    }

    /// Returns true if the x axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn get_x_motion_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_motion_inverted
    }

    /// Set whether the y axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn y_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_motion_inverted = invert;
        self
    }

    /// Returns true if the y axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn get_y_motion_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_motion_inverted
    }

    /// Sets the viewport size used for mouse position calculations.
    pub fn viewport_size(mut self, size: Size) -> Self {
        self.mouse_data.viewport_size = sz;
        self
    }

    /// Returns the currently set viewport size.
    pub fn get_viewport_size(&self) -> &Size {
        &self.mouse_data.viewport_size
    }

    /// Add an association between the Button and Action in the built object.
    pub fn with_action_mapping(mut self, button: Button, action: A) -> Self {
        self.input_remappings.push((but, act));
        self
    }

    /// Creates an `InputTranslator` from this builder object. Consumes self.
    pub fn build_translator(self) -> InputTranslator<A> { self.into() }

    /// Creates an `InputRebind` from this builder object. Consumes self.
    pub fn build_rebind(self) -> InputRebind<A> { self.into() }
}

impl<A: Action> Default for RebindBuilder<A> {
    /// Creates a new `RebindBuilder`. The viewport size is set to (800, 600).
    fn default() -> Self {
        Self::new((800, 600).into())
    }
}

impl<A: Action> Into<InputTranslator<A>> for RebindBuilder<A> {
    fn into(self) -> InputTranslator<A> {
        let mut input_map = InputTranslator::new(self.mouse_data.viewport_size);

        input_map.mouse_translator.data = self.mouse_data;
        input_map.keymap = self.input_remappings.iter().cloned().collect();

        input_map
    }
}

impl<A: Action> Into<InputRebind<A>> for RebindBuilder<A> {
    #[allow(dead_code, unused_variables, unreachable_code)]
    fn into(self) -> InputRebind<A> {
        unimplemented!();

        let mut input_rebind = InputRebind::new(self.mouse_data.viewport_size);

        input_rebind.mouse_data = self.mouse_data;
        //input_rebind.keymap.btn_map = self.input_remappings.iter().map(|x| x.clone()).collect();

        input_rebind
    }
}

<commit_msg>Update method bodies to use the parameter names changed in 28f1e7f<commit_after>use {Action, InputTranslator, InputRebind, MouseTranslationData};
use input::Button;
use window::Size;
use std::convert::Into;
use std::default::Default;

/// Convenience object for constructing an InputMap.
pub struct RebindBuilder<A: Action> {
    input_remappings: Vec<(Button, A)>,
    mouse_data: MouseTranslationData
}

impl<A: Action> RebindBuilder<A> {
    /// Creates a new `RebindBuilder` with the specified viewport size.
    pub fn new(size: Size) -> Self {
        RebindBuilder {
            input_remappings: vec![],
            mouse_data: MouseTranslationData::new(size)
        }
    }

    /// Set whether the x scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn x_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_scroll_inverted = invert;
        self
    }

    /// Returns true if the x scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn get_x_scroll_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_scroll_inverted
    }

    /// Set whether the y scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn y_scroll_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_scroll_inverted = invert;
        self
    }

    /// Returns true if the y scroll is inverted on the builder (and thus
    /// on the built object).
    pub fn get_y_scroll_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_scroll_inverted
    }

    /// Set whether the x axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn x_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.x_axis_motion_inverted = invert;
        self
    }

    /// Returns true if the x axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn get_x_motion_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_motion_inverted
    }

    /// Set whether the y axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn y_motion_inverted(mut self, invert: bool) -> Self {
        self.mouse_data.y_axis_motion_inverted = invert;
        self
    }

    /// Returns true if the y axis motion is inverted on the builder (and thus
    /// on the built object).
    pub fn get_y_motion_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_motion_inverted
    }

    /// Sets the viewport size used for mouse position calculations.
    pub fn viewport_size(mut self, size: Size) -> Self {
        self.mouse_data.viewport_size = size;
        self
    }

    /// Returns the currently set viewport size.
    pub fn get_viewport_size(&self) -> &Size {
        &self.mouse_data.viewport_size
    }

    /// Add an association between the Button and Action in the built object.
    pub fn with_action_mapping(mut self, button: Button, action: A) -> Self {
        self.input_remappings.push((button, action));
        self
    }

    /// Creates an `InputTranslator` from this builder object. Consumes self.
    pub fn build_translator(self) -> InputTranslator<A> { self.into() }

    /// Creates an `InputRebind` from this builder object. Consumes self.
    pub fn build_rebind(self) -> InputRebind<A> { self.into() }
}

impl<A: Action> Default for RebindBuilder<A> {
    /// Creates a new `RebindBuilder`. The viewport size is set to (800, 600).
    fn default() -> Self {
        Self::new((800, 600).into())
    }
}

impl<A: Action> Into<InputTranslator<A>> for RebindBuilder<A> {
    fn into(self) -> InputTranslator<A> {
        let mut input_map = InputTranslator::new(self.mouse_data.viewport_size);

        input_map.mouse_translator.data = self.mouse_data;
        input_map.keymap = self.input_remappings.iter().cloned().collect();

        input_map
    }
}

impl<A: Action> Into<InputRebind<A>> for RebindBuilder<A> {
    #[allow(dead_code, unused_variables, unreachable_code)]
    fn into(self) -> InputRebind<A> {
        unimplemented!();

        let mut input_rebind = InputRebind::new(self.mouse_data.viewport_size);

        input_rebind.mouse_data = self.mouse_data;
        //input_rebind.keymap.btn_map = self.input_remappings.iter().map(|x| x.clone()).collect();

        input_rebind
    }
}

<|endoftext|>


<commit_before>use libc::{c_char, c_uint};
use ffi::prelude::{LLVMBuilderRef, LLVMValueRef};
use ffi::{core, LLVMBuilder, LLVMRealPredicate, LLVMIntPredicate};
use cbox::{CSemiBox, DisposeRef};
use std::mem;
use block::BasicBlock;
use context::Context;
use ty::Type;
use value::{Function, Value, Predicate};

static NULL_NAME:[c_char; 1] = [0];

/// This provides a uniform API for creating instructions and inserting them into a basic block.
pub struct Builder;
native_ref!(&Builder = LLVMBuilderRef);
macro_rules! bin_op(
    ($name:ident, $func:ident) => (
        pub fn $name(&self, left: &Value, right: &Value) -> &Value {
            unsafe { core::$func(self.into(), left.into(), right.into(), NULL_NAME.as_ptr()) }.into()
        }
    );
    ($name:ident, $ifunc:ident, $ffunc:ident) => (
        pub fn $name(&self, left: &Value, right: &Value) -> &Value {
            let ty = left.get_type();
            unsafe {
                (if ty.is_integer() {
                    core::$ifunc
                } else {
                    core::$ffunc
                })(self.into(), left.into(), right.into(), NULL_NAME.as_ptr()).into()
            }
        }
    );
);
macro_rules! un_op(
    ($name:ident, $func:ident) => (
        pub fn $name(&self, value: &Value) -> &Value {
            unsafe { core::$func(self.into(), value.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
        }
    );
);
impl Builder {
    /// Create a new builder in the context given.
    pub fn new(context: &Context) -> CSemiBox<Builder> {
        CSemiBox::new(unsafe { core::LLVMCreateBuilderInContext(context.into()) }.into())
    }
    /// Position the builder at the end of `block`.
    pub fn position_at_end(&self, block: &BasicBlock) {
        unsafe { core::LLVMPositionBuilderAtEnd(self.into(), block.into()) }
    }
    /// Build an instruction that returns from the function with void.
    pub fn build_ret_void(&self) -> &Value {
        unsafe { core::LLVMBuildRetVoid(self.into()) }.into()
    }
    /// Build an instruction that returns from the function with `value`.
    pub fn build_ret(&self, value: &Value) -> &Value {
        unsafe { core::LLVMBuildRet(self.into(), value.into()) }.into()
    }
    /// Build an instruction that allocates an array with the element type `elem` and the size `size`.
    ///
    /// The size of this array will be the size of `elem` times `size`.
    pub fn build_array_alloca(&self, elem: &Type, size: &Value) -> &Value {
        unsafe { core::LLVMBuildArrayAlloca(self.into(), elem.into(), size.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
    }
    /// Build an instruction that allocates a pointer to fit the size of `ty` then returns this pointer.
    pub fn build_alloca(&self, ty: &Type) -> &Value {
        unsafe { core::LLVMBuildAlloca(self.into(), ty.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
    }
    /// Build an instruction that frees the `val`, which _MUST_ be a pointer that was returned
    /// from `build_alloca`.
    pub fn build_free(&self, val: &Value) -> &Value {
        unsafe { core::LLVMBuildFree(self.into(), val.into()) }.into()
    }
    /// Build an instruction that store the value `val` in the pointer `ptr`.
    pub fn build_store(&self, val: &Value, ptr: &Value) -> &Value {
        unsafe { core::LLVMBuildStore(self.into(), val.into(), ptr.into()) }.into()
    }
    /// Build an instruction that branches to the block `dest`.
    pub fn build_br(&self, dest: &BasicBlock) -> &Value {
        unsafe { core::LLVMBuildBr(self.into(), dest.into()).into() }
    }
    /// Build an instruction that branches to `if_block` if `cond` evaluates to true, and `else_block` otherwise.
    pub fn build_cond_br(&self, cond: &Value, if_block: &BasicBlock, else_block: Option<&BasicBlock>) -> &Value {
        unsafe { core::LLVMBuildCondBr(self.into(), cond.into(), if_block.into(), mem::transmute(else_block)).into() }
    }
    /// Build an instruction that calls the function `func` with the arguments `args`.
    ///
    /// This will return the return value of the function.
    pub fn build_call(&self, func: &Function, args: &[&Value]) -> &Value {
        unsafe {
            let call = core::LLVMBuildCall(self.into(), func.into(), args.as_ptr() as *mut LLVMValueRef, args.len() as c_uint, NULL_NAME.as_ptr());
            core::LLVMSetTailCall(call, 0);
            call.into()
        }
    }
    /// Build an instruction that calls the function `func` with the arguments `args`.
    ///
    /// This will return the return value of the function.
    pub fn build_tail_call(&self, func: &Function, args: &[&Value]) -> &Value {
        unsafe {
            let call = core::LLVMBuildCall(self.into(), func.into(), args.as_ptr() as *mut LLVMValueRef, args.len() as c_uint, NULL_NAME.as_ptr());
            core::LLVMSetTailCall(call, 1);
            call.into()
        }
    }
    /// Build an instruction that yields to `true_val` if `cond` is equal to `1`, and `false_val` otherwise.
    pub fn build_select(&self, cond: &Value, true_val: &Value, false_val: &Value) -> &Value {
        unsafe { core::LLVMBuildSelect(self.into(), cond.into(), true_val.into(), false_val.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that casts a value into a certain type.
    pub fn build_bit_cast(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildBitCast(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that truncates the high-order bits of value to fit into a certain type.
    pub fn build_trunc(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildTrunc(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that inserts a value into an aggregate data value.
    pub fn build_insert_value(&self, agg: &Value, elem: &Value, index: usize) -> &Value {
        unsafe { core::LLVMBuildInsertValue(self.into(), agg.into(), elem.into(), index as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that extracts a value from an aggregate data value.
    pub fn build_extract_value(&self, agg: &Value, index: usize) -> &Value {
        unsafe { core::LLVMBuildExtractValue(self.into(), agg.into(), index as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that computes the address of a subelement of an aggregate data structure.
    ///
    /// Basically type-safe pointer arithmetic.
    pub fn build_gep(&self, pointer: &Value, indices: &[&Value]) -> &Value {
        unsafe { core::LLVMBuildInBoundsGEP(self.into(), pointer.into(), indices.as_ptr() as *mut LLVMValueRef, indices.len() as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that runs whichever block matches the value, or `default` if none of them matched it.
    pub fn build_switch(&self, value: &Value, default: &BasicBlock, cases: &[(&Value, &BasicBlock)]) -> &Value {
        unsafe {
            let switch = core::LLVMBuildSwitch(self.into(), value.into(), default.into(), cases.len() as c_uint);
            for case in cases {
                core::LLVMAddCase(switch, case.0.into(), case.1.into());
            }
            switch.into()
        }
    }
    un_op!{build_load, LLVMBuildLoad}
    un_op!{build_neg, LLVMBuildNeg}
    un_op!{build_not, LLVMBuildNot}
    bin_op!{build_add, LLVMBuildAdd, LLVMBuildFAdd}
    bin_op!{build_sub, LLVMBuildSub, LLVMBuildFSub}
    bin_op!{build_mul, LLVMBuildMul, LLVMBuildFMul}
    bin_op!{build_div, LLVMBuildSDiv, LLVMBuildFDiv}
    bin_op!{build_shl, LLVMBuildShl}
    bin_op!{build_ashr, LLVMBuildAShr}
    bin_op!{build_and, LLVMBuildAnd}
    bin_op!{build_or, LLVMBuildOr}
    /// Build an instruction to compare two values with the predicate given.
    pub fn build_cmp(&self, a: &Value, b: &Value, pred: Predicate) -> &Value {
        let (at, bt) = (a.get_type(), b.get_type());
        assert_eq!(at, bt);
        if at.is_integer() {
            let pred = match pred {
                Predicate::Equal => LLVMIntPredicate::LLVMIntEQ,
                Predicate::NotEqual => LLVMIntPredicate::LLVMIntNE,
                Predicate::GreaterThan => LLVMIntPredicate::LLVMIntSGT,
                Predicate::GreaterThanOrEqual => LLVMIntPredicate::LLVMIntSGE,
                Predicate::LessThan => LLVMIntPredicate::LLVMIntSLT,
                Predicate::LessThanOrEqual => LLVMIntPredicate::LLVMIntSLE
            };
            unsafe { core::LLVMBuildICmp(self.into(), pred, a.into(), b.into(), NULL_NAME.as_ptr()) }.into()
        } else if at.is_float() {
            let pred = match pred {
                Predicate::Equal => LLVMRealPredicate::LLVMRealOEQ,
                Predicate::NotEqual => LLVMRealPredicate::LLVMRealONE,
                Predicate::GreaterThan => LLVMRealPredicate::LLVMRealOGT,
                Predicate::GreaterThanOrEqual => LLVMRealPredicate::LLVMRealOGE,
                Predicate::LessThan => LLVMRealPredicate::LLVMRealOLT,
                Predicate::LessThanOrEqual => LLVMRealPredicate::LLVMRealOLE
            };
            unsafe { core::LLVMBuildFCmp(self.into(), pred, a.into(), b.into(), NULL_NAME.as_ptr()) }.into()
        } else {
            panic!("expected numbers, got {:?}", at)
        }
    }
}

impl DisposeRef for Builder {
    type RefTo = LLVMBuilder;
    #[inline(always)]
    unsafe fn dispose(ptr: LLVMBuilderRef) {
        core::LLVMDisposeBuilder(ptr)
    }
}
<commit_msg>added int_to_ptr to builder<commit_after>use libc::{c_char, c_uint};
use ffi::prelude::{LLVMBuilderRef, LLVMValueRef};
use ffi::{core, LLVMBuilder, LLVMRealPredicate, LLVMIntPredicate};
use cbox::{CSemiBox, DisposeRef};
use std::mem;
use block::BasicBlock;
use context::Context;
use ty::Type;
use value::{Function, Value, Predicate};

static NULL_NAME:[c_char; 1] = [0];

/// This provides a uniform API for creating instructions and inserting them into a basic block.
pub struct Builder;
native_ref!(&Builder = LLVMBuilderRef);
macro_rules! bin_op(
    ($name:ident, $func:ident) => (
        pub fn $name(&self, left: &Value, right: &Value) -> &Value {
            unsafe { core::$func(self.into(), left.into(), right.into(), NULL_NAME.as_ptr()) }.into()
        }
    );
    ($name:ident, $ifunc:ident, $ffunc:ident) => (
        pub fn $name(&self, left: &Value, right: &Value) -> &Value {
            let ty = left.get_type();
            unsafe {
                (if ty.is_integer() {
                    core::$ifunc
                } else {
                    core::$ffunc
                })(self.into(), left.into(), right.into(), NULL_NAME.as_ptr()).into()
            }
        }
    );
);
macro_rules! un_op(
    ($name:ident, $func:ident) => (
        pub fn $name(&self, value: &Value) -> &Value {
            unsafe { core::$func(self.into(), value.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
        }
    );
);
impl Builder {
    /// Create a new builder in the context given.
    pub fn new(context: &Context) -> CSemiBox<Builder> {
        CSemiBox::new(unsafe { core::LLVMCreateBuilderInContext(context.into()) }.into())
    }
    /// Position the builder at the end of `block`.
    pub fn position_at_end(&self, block: &BasicBlock) {
        unsafe { core::LLVMPositionBuilderAtEnd(self.into(), block.into()) }
    }
    /// Build an instruction that returns from the function with void.
    pub fn build_ret_void(&self) -> &Value {
        unsafe { core::LLVMBuildRetVoid(self.into()) }.into()
    }
    /// Build an instruction that returns from the function with `value`.
    pub fn build_ret(&self, value: &Value) -> &Value {
        unsafe { core::LLVMBuildRet(self.into(), value.into()) }.into()
    }
    /// Build an instruction that allocates an array with the element type `elem` and the size `size`.
    ///
    /// The size of this array will be the size of `elem` times `size`.
    pub fn build_array_alloca(&self, elem: &Type, size: &Value) -> &Value {
        unsafe { core::LLVMBuildArrayAlloca(self.into(), elem.into(), size.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
    }
    /// Build an instruction that allocates a pointer to fit the size of `ty` then returns this pointer.
    pub fn build_alloca(&self, ty: &Type) -> &Value {
        unsafe { core::LLVMBuildAlloca(self.into(), ty.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
    }
    /// Build an instruction that frees the `val`, which _MUST_ be a pointer that was returned
    /// from `build_alloca`.
    pub fn build_free(&self, val: &Value) -> &Value {
        unsafe { core::LLVMBuildFree(self.into(), val.into()) }.into()
    }
    /// Build an instruction that store the value `val` in the pointer `ptr`.
    pub fn build_store(&self, val: &Value, ptr: &Value) -> &Value {
        unsafe { core::LLVMBuildStore(self.into(), val.into(), ptr.into()) }.into()
    }
    /// Build an instruction that branches to the block `dest`.
    pub fn build_br(&self, dest: &BasicBlock) -> &Value {
        unsafe { core::LLVMBuildBr(self.into(), dest.into()).into() }
    }
    /// Build an instruction that branches to `if_block` if `cond` evaluates to true, and `else_block` otherwise.
    pub fn build_cond_br(&self, cond: &Value, if_block: &BasicBlock, else_block: Option<&BasicBlock>) -> &Value {
        unsafe { core::LLVMBuildCondBr(self.into(), cond.into(), if_block.into(), mem::transmute(else_block)).into() }
    }
    /// Build an instruction that calls the function `func` with the arguments `args`.
    ///
    /// This will return the return value of the function.
    pub fn build_call(&self, func: &Function, args: &[&Value]) -> &Value {
        unsafe {
            let call = core::LLVMBuildCall(self.into(), func.into(), args.as_ptr() as *mut LLVMValueRef, args.len() as c_uint, NULL_NAME.as_ptr());
            core::LLVMSetTailCall(call, 0);
            call.into()
        }
    }
    /// Build an instruction that calls the function `func` with the arguments `args`.
    ///
    /// This will return the return value of the function.
    pub fn build_tail_call(&self, func: &Function, args: &[&Value]) -> &Value {
        unsafe {
            let call = core::LLVMBuildCall(self.into(), func.into(), args.as_ptr() as *mut LLVMValueRef, args.len() as c_uint, NULL_NAME.as_ptr());
            core::LLVMSetTailCall(call, 1);
            call.into()
        }
    }
    /// Build an instruction that yields to `true_val` if `cond` is equal to `1`, and `false_val` otherwise.
    pub fn build_select(&self, cond: &Value, true_val: &Value, false_val: &Value) -> &Value {
        unsafe { core::LLVMBuildSelect(self.into(), cond.into(), true_val.into(), false_val.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that casts a value into a certain type.
    pub fn build_bit_cast(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildBitCast(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    pub fn build_int_to_ptr(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildIntToPtr(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that truncates the high-order bits of value to fit into a certain type.
    pub fn build_trunc(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildTrunc(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that inserts a value into an aggregate data value.
    pub fn build_insert_value(&self, agg: &Value, elem: &Value, index: usize) -> &Value {
        unsafe { core::LLVMBuildInsertValue(self.into(), agg.into(), elem.into(), index as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that extracts a value from an aggregate data value.
    pub fn build_extract_value(&self, agg: &Value, index: usize) -> &Value {
        unsafe { core::LLVMBuildExtractValue(self.into(), agg.into(), index as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that computes the address of a subelement of an aggregate data structure.
    ///
    /// Basically type-safe pointer arithmetic.
    pub fn build_gep(&self, pointer: &Value, indices: &[&Value]) -> &Value {
        unsafe { core::LLVMBuildInBoundsGEP(self.into(), pointer.into(), indices.as_ptr() as *mut LLVMValueRef, indices.len() as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that runs whichever block matches the value, or `default` if none of them matched it.
    pub fn build_switch(&self, value: &Value, default: &BasicBlock, cases: &[(&Value, &BasicBlock)]) -> &Value {
        unsafe {
            let switch = core::LLVMBuildSwitch(self.into(), value.into(), default.into(), cases.len() as c_uint);
            for case in cases {
                core::LLVMAddCase(switch, case.0.into(), case.1.into());
            }
            switch.into()
        }
    }
    un_op!{build_load, LLVMBuildLoad}
    un_op!{build_neg, LLVMBuildNeg}
    un_op!{build_not, LLVMBuildNot}
    bin_op!{build_add, LLVMBuildAdd, LLVMBuildFAdd}
    bin_op!{build_sub, LLVMBuildSub, LLVMBuildFSub}
    bin_op!{build_mul, LLVMBuildMul, LLVMBuildFMul}
    bin_op!{build_div, LLVMBuildSDiv, LLVMBuildFDiv}
    bin_op!{build_shl, LLVMBuildShl}
    bin_op!{build_ashr, LLVMBuildAShr}
    bin_op!{build_and, LLVMBuildAnd}
    bin_op!{build_or, LLVMBuildOr}
    /// Build an instruction to compare two values with the predicate given.
    pub fn build_cmp(&self, a: &Value, b: &Value, pred: Predicate) -> &Value {
        let (at, bt) = (a.get_type(), b.get_type());
        assert_eq!(at, bt);
        if at.is_integer() {
            let pred = match pred {
                Predicate::Equal => LLVMIntPredicate::LLVMIntEQ,
                Predicate::NotEqual => LLVMIntPredicate::LLVMIntNE,
                Predicate::GreaterThan => LLVMIntPredicate::LLVMIntSGT,
                Predicate::GreaterThanOrEqual => LLVMIntPredicate::LLVMIntSGE,
                Predicate::LessThan => LLVMIntPredicate::LLVMIntSLT,
                Predicate::LessThanOrEqual => LLVMIntPredicate::LLVMIntSLE
            };
            unsafe { core::LLVMBuildICmp(self.into(), pred, a.into(), b.into(), NULL_NAME.as_ptr()) }.into()
        } else if at.is_float() {
            let pred = match pred {
                Predicate::Equal => LLVMRealPredicate::LLVMRealOEQ,
                Predicate::NotEqual => LLVMRealPredicate::LLVMRealONE,
                Predicate::GreaterThan => LLVMRealPredicate::LLVMRealOGT,
                Predicate::GreaterThanOrEqual => LLVMRealPredicate::LLVMRealOGE,
                Predicate::LessThan => LLVMRealPredicate::LLVMRealOLT,
                Predicate::LessThanOrEqual => LLVMRealPredicate::LLVMRealOLE
            };
            unsafe { core::LLVMBuildFCmp(self.into(), pred, a.into(), b.into(), NULL_NAME.as_ptr()) }.into()
        } else {
            panic!("expected numbers, got {:?}", at)
        }
    }
}

impl DisposeRef for Builder {
    type RefTo = LLVMBuilder;
    #[inline(always)]
    unsafe fn dispose(ptr: LLVMBuilderRef) {
        core::LLVMDisposeBuilder(ptr)
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::Named;
use intern::string_key::Intern;
use lsp_types::request::Request;
use schema::Schema;
use schema_documentation::SchemaDocumentation;
use serde::Deserialize;
use serde::Serialize;

use crate::lsp_runtime_error::LSPRuntimeResult;
use crate::server::GlobalState;

pub(crate) enum SearchSchemaItems {}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaSearchItem {
    name: String,
    description: Option<String>,
}

#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SearchSchemaItemsResponse {
    pub items: Vec<SchemaSearchItem>,
    pub has_more: bool,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SearchSchemaItemsParams {
    pub filter: Option<String>,
    pub schema_name: String,
    pub take: Option<usize>,
    pub skip: Option<usize>,
}

impl Request for SearchSchemaItems {
    type Params = SearchSchemaItemsParams;
    type Result = SearchSchemaItemsResponse;
    const METHOD: &'static str = "relay/searchSchemaItems";
}

pub(crate) fn on_search_schema_items(
    state: &impl GlobalState,
    params: SearchSchemaItemsParams,
) -> LSPRuntimeResult<<SearchSchemaItems as Request>::Result> {
    let filter = params.filter.map(|f| f.to_lowercase());
    let schema_name: &str = &params.schema_name;
    let schema = state.get_schema(&schema_name.intern())?;

    let schema_documentation = state.get_schema_documentation(schema_name);

    let objects = filter_and_transform_items(schema.objects(), &schema_documentation, &filter);
    let interfaces =
        filter_and_transform_items(schema.interfaces(), &schema_documentation, &filter);
    let enums = filter_and_transform_items(schema.enums(), &schema_documentation, &filter);
    let unions = filter_and_transform_items(schema.unions(), &schema_documentation, &filter);
    let input_objects =
        filter_and_transform_items(schema.input_objects(), &schema_documentation, &filter);
    let scalars = filter_and_transform_items(schema.scalars(), &schema_documentation, &filter);

    let mut items = objects
        .chain(interfaces)
        .chain(enums)
        .chain(unions)
        .chain(input_objects)
        .chain(scalars)
        .collect::<Vec<_>>();

    items.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));

    let skip = *params.skip.as_ref().unwrap_or(&0);
    let take = *params.take.as_ref().unwrap_or(&500);
    let has_more = items.len() > skip + take;

    let items = items.into_iter().skip(skip).take(take).collect::<Vec<_>>();

    Ok(SearchSchemaItemsResponse { items, has_more })
}

fn filter_and_transform_items<'a, T: Named + 'a>(
    items: impl Iterator<Item = &'a T> + 'a,
    schema_documentation: &'a impl SchemaDocumentation,
    filter: &'a Option<String>,
) -> impl Iterator<Item = SchemaSearchItem> + 'a {
    items.filter_map(move |obj| {
        let name = obj.name().lookup();
        let description = schema_documentation
            .get_type_description(name)
            .map(|s| s.to_string());

        if should_include_named_item(name, &description, filter) {
            Some(SchemaSearchItem {
                name: name.to_string(),
                description,
            })
        } else {
            None
        }
    })
}

fn should_include_named_item(
    name: &str,
    description: &Option<String>,
    filter: &Option<String>,
) -> bool {
    if let Some(filter) = filter.as_ref() {
        if name.to_lowercase().contains(filter) {
            true
        } else if let Some(description) = description {
            description.to_lowercase().contains(filter)
        } else {
            false
        }
    } else {
        true
    }
}

#[cfg(test)]
mod tests {
    use crate::search_schema_items::should_include_named_item;

    #[test]
    fn test_no_filter() {
        assert_eq!(
            should_include_named_item("Yohan Blake", &Some("London 2012".to_string()), &None),
            true
        );
        assert_eq!(should_include_named_item("Usain Bolt", &None, &None), true);
    }

    #[test]
    fn test_filter_matches_name() {
        assert_eq!(
            should_include_named_item("Michael Frater", &None, &Some("michael".to_string())),
            true
        );
        assert_eq!(
            should_include_named_item(
                "Nesta Carter",
                &Some("London 2012".to_string()),
                &Some("nesta".to_string())
            ),
            true
        );
    }

    #[test]
    fn test_filter_matches_description() {
        assert_eq!(
            should_include_named_item(
                "Tiana Bartoletta",
                &Some("London 2012".to_string()),
                &Some("london".to_string())
            ),
            true
        );
    }

    #[test]
    fn test_non_matching_filter() {
        assert_eq!(
            should_include_named_item(
                "Allyson Felix",
                &Some("London 2012".to_string()),
                &Some("salt lake city".to_string())
            ),
            false
        );
    }
}
<commit_msg>Refactor `filter_and_transform_items` to not rely on Named<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use graphql_ir::reexport::StringKey;
use intern::string_key::Intern;
use lsp_types::request::Request;
use schema::Schema;
use schema_documentation::SchemaDocumentation;
use serde::Deserialize;
use serde::Serialize;

use crate::lsp_runtime_error::LSPRuntimeResult;
use crate::server::GlobalState;

pub(crate) enum SearchSchemaItems {}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaSearchItem {
    name: String,
    description: Option<String>,
}

#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SearchSchemaItemsResponse {
    pub items: Vec<SchemaSearchItem>,
    pub has_more: bool,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SearchSchemaItemsParams {
    pub filter: Option<String>,
    pub schema_name: String,
    pub take: Option<usize>,
    pub skip: Option<usize>,
}

impl Request for SearchSchemaItems {
    type Params = SearchSchemaItemsParams;
    type Result = SearchSchemaItemsResponse;
    const METHOD: &'static str = "relay/searchSchemaItems";
}

pub(crate) fn on_search_schema_items(
    state: &impl GlobalState,
    params: SearchSchemaItemsParams,
) -> LSPRuntimeResult<<SearchSchemaItems as Request>::Result> {
    let filter = params.filter.map(|f| f.to_lowercase());
    let schema_name: &str = &params.schema_name;
    let schema = state.get_schema(&schema_name.intern())?;

    let schema_documentation = state.get_schema_documentation(schema_name);

    let objects = filter_and_transform_items(
        schema.objects().map(|o| o.name.item),
        &schema_documentation,
        &filter,
    );
    let interfaces = filter_and_transform_items(
        schema.interfaces().map(|i| i.name.item),
        &schema_documentation,
        &filter,
    );
    let enums = filter_and_transform_items(
        schema.enums().map(|e| e.name.item),
        &schema_documentation,
        &filter,
    );
    let unions = filter_and_transform_items(
        schema.unions().map(|u| u.name.item),
        &schema_documentation,
        &filter,
    );
    let input_objects = filter_and_transform_items(
        schema.input_objects().map(|io| io.name.item),
        &schema_documentation,
        &filter,
    );
    let scalars = filter_and_transform_items(
        schema.scalars().map(|s| s.name.item),
        &schema_documentation,
        &filter,
    );

    let mut items = objects
        .chain(interfaces)
        .chain(enums)
        .chain(unions)
        .chain(input_objects)
        .chain(scalars)
        .collect::<Vec<_>>();

    items.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));

    let skip = *params.skip.as_ref().unwrap_or(&0);
    let take = *params.take.as_ref().unwrap_or(&500);
    let has_more = items.len() > skip + take;

    let items = items.into_iter().skip(skip).take(take).collect::<Vec<_>>();

    Ok(SearchSchemaItemsResponse { items, has_more })
}

fn filter_and_transform_items<'a>(
    items: impl Iterator<Item = StringKey> + 'a,
    schema_documentation: &'a impl SchemaDocumentation,
    filter: &'a Option<String>,
) -> impl Iterator<Item = SchemaSearchItem> + 'a {
    items.filter_map(move |obj| {
        let name = obj.lookup();
        let description = schema_documentation
            .get_type_description(name)
            .map(|s| s.to_string());

        if should_include_named_item(name, &description, filter) {
            Some(SchemaSearchItem {
                name: name.to_string(),
                description,
            })
        } else {
            None
        }
    })
}

fn should_include_named_item(
    name: &str,
    description: &Option<String>,
    filter: &Option<String>,
) -> bool {
    if let Some(filter) = filter.as_ref() {
        if name.to_lowercase().contains(filter) {
            true
        } else if let Some(description) = description {
            description.to_lowercase().contains(filter)
        } else {
            false
        }
    } else {
        true
    }
}

#[cfg(test)]
mod tests {
    use crate::search_schema_items::should_include_named_item;

    #[test]
    fn test_no_filter() {
        assert_eq!(
            should_include_named_item("Yohan Blake", &Some("London 2012".to_string()), &None),
            true
        );
        assert_eq!(should_include_named_item("Usain Bolt", &None, &None), true);
    }

    #[test]
    fn test_filter_matches_name() {
        assert_eq!(
            should_include_named_item("Michael Frater", &None, &Some("michael".to_string())),
            true
        );
        assert_eq!(
            should_include_named_item(
                "Nesta Carter",
                &Some("London 2012".to_string()),
                &Some("nesta".to_string())
            ),
            true
        );
    }

    #[test]
    fn test_filter_matches_description() {
        assert_eq!(
            should_include_named_item(
                "Tiana Bartoletta",
                &Some("London 2012".to_string()),
                &Some("london".to_string())
            ),
            true
        );
    }

    #[test]
    fn test_non_matching_filter() {
        assert_eq!(
            should_include_named_item(
                "Allyson Felix",
                &Some("London 2012".to_string()),
                &Some("salt lake city".to_string())
            ),
            false
        );
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::match_::hash_supported_argument;

use super::*;
use common::{sync::try_join, DiagnosticsResult, PerfLogEvent, PerfLogger};
use fnv::FnvHashSet;
use graphql_ir::Program;
use interner::StringKey;
use regex::Regex;
use std::sync::Arc;

#[derive(Debug)]
pub struct Programs {
    pub source: Arc<Program>,
    pub reader: Arc<Program>,
    pub normalization: Arc<Program>,
    pub operation_text: Arc<Program>,
    pub typegen: Arc<Program>,
}

pub fn apply_transforms<TPerfLogger>(
    project_name: StringKey,
    program: Arc<Program>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    test_path_regex: &Option<Regex>,
    perf_logger: Arc<TPerfLogger>,
    print_stats: Option<fn(extra_info: &'static str, program: &Program) -> ()>,
) -> DiagnosticsResult<Programs>
where
    TPerfLogger: PerfLogger + 'static,
{
    // The execution pipeline is as follows, where items at the same indentation
    // can be computed independently and therefore in parallel:
    // |- common
    //    |- reader
    //    |- operation
    //       |- normalization
    //       |- operation_text
    // |- typegen
    //
    // NOTE: try_join(f1, f2) prefers the errors from f1 over f2, so process the normalization
    // program first since it is likely to include more errors (both per-fragment and
    // whole-operation errors) than the reader_program which just includes per-fragment errors.
    let (((normalization_program, text_program), reader_program), typegen_program) = try_join(
        || {
            let common_program = apply_common_transforms(
                project_name,
                Arc::clone(&program),
                connection_interface,
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )?;

            try_join(
                || {
                    let operation_program = apply_operation_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        connection_interface,
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )?;

                    try_join(
                        || {
                            apply_normalization_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&base_fragment_names),
                                Arc::clone(&feature_flags),
                                test_path_regex.clone(),
                                Arc::clone(&perf_logger),
                                print_stats,
                            )
                        },
                        || {
                            apply_operation_text_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&base_fragment_names),
                                Arc::clone(&feature_flags),
                                Arc::clone(&perf_logger),
                            )
                        },
                    )
                },
                || {
                    apply_reader_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        Arc::clone(&feature_flags),
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )
                },
            )
        },
        || {
            apply_typegen_transforms(
                project_name,
                Arc::clone(&program),
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )
        },
    )?;

    Ok(Programs {
        source: program,
        reader: reader_program,
        normalization: normalization_program,
        operation_text: text_program,
        typegen: typegen_program,
    })
}

/// Applies transforms that apply to every output.
fn apply_common_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_common_transforms");
    log_event.string("project", project_name.to_string());
    let mut program = log_event.time("transform_connections", || {
        transform_connections(&program, connection_interface)
    });
    program = log_event.time("mask", || mask(&program));
    program = log_event.time("transform_defer_stream", || {
        transform_defer_stream(&program)
    })?;
    program = log_event.time("transform_match", || {
        transform_match(&program, &feature_flags)
    })?;
    program = log_event.time("transform_subscriptions", || {
        transform_subscriptions(&program)
    })?;
    program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, false)
    })?;

    program = log_event.time("relay_resolvers", || {
        relay_resolvers(&program, feature_flags.enable_relay_resolver_transform)
    })?;

    if feature_flags.enable_flight_transform {
        program = log_event.time("react_flight", || react_flight(&program))?;
        program = log_event.time("relay_client_component", || {
            relay_client_component(&program, &feature_flags)
        })?;
    }

    program = log_event.time("relay_actor_change_transform", || {
        relay_actor_change_transform(&program, &feature_flags.actor_change_support)
    })?;

    log_event.complete();

    Ok(Arc::new(program))
}

/// Applies transforms only for generated reader code.
/// Corresponds to the "fragment transforms" in the JS compiler.
fn apply_reader_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_reader_transforms");
    log_event.string("project", project_name.to_string());
    let mut program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;

    program = log_event.time("client_extensions", || client_extensions(&program));
    program = log_event.time("handle_field_transform", || {
        handle_field_transform(&program)
    });
    program = log_event.time("inline_data_fragment", || inline_data_fragment(&program))?;
    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program))?;
    program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });
    log_event.time("flatten", || flatten(&mut program, true, false))?;
    program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    program = log_event.time("generate_data_driven_dependency_metadata", || {
        generate_data_driven_dependency_metadata(&program)
    });
    program = log_event.time("hash_supported_argument", || {
        hash_supported_argument(&program, &feature_flags)
    })?;

    log_event.complete();

    Ok(Arc::new(program))
}

/// Applies transforms that apply to all operation artifacts.
/// Corresponds to the "query transforms" in the JS compiler.
fn apply_operation_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_operation_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("split_module_import", || {
        split_module_import(&program, &base_fragment_names)
    });
    program = log_event.time("generate_id_field", || generate_id_field(&program));
    program = log_event.time("declarative_connection", || {
        transform_declarative_connection(&program, connection_interface)
    })?;

    // TODO(T67052528): execute FB-specific transforms only if config options is provided
    program = log_event.time("generate_subscription_name_metadata", || {
        generate_subscription_name_metadata(&program)
    })?;
    program = log_event.time("generate_live_query_metadata", || {
        generate_live_query_metadata(&program)
    })?;

    log_event.complete();

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the generated normalization code.
///
/// Corresponds to the "codegen transforms" in the JS compiler
fn apply_normalization_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    feature_flags: Arc<FeatureFlags>,
    test_path_regex: Option<Regex>,
    perf_logger: Arc<impl PerfLogger>,
    maybe_print_stats: Option<fn(extra_info: &'static str, program: &Program) -> ()>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_normalization_transforms");
    log_event.string("project", project_name.to_string());
    if let Some(print_stats) = maybe_print_stats {
        print_stats("normalization start", &program);
    }

    let mut program = log_event.time("apply_fragment_arguments", || {
        apply_fragment_arguments(
            &program,
            true,
            &feature_flags.no_inline,
            &base_fragment_names,
        )
    })?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("apply_fragment_arguments", &program);
    }
    program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("relay_early_flush", &program);
    }

    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program))?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("skip_unreachable_node", &program);
    }

    program = log_event.time("inline_fragments", || inline_fragments(&program));
    if let Some(print_stats) = maybe_print_stats {
        print_stats("inline_fragments", &program);
    }

    program = log_event.time("client_extensions", || client_extensions(&program));
    if let Some(print_stats) = maybe_print_stats {
        print_stats("client_extensions", &program);
    }

    program = log_event.time("generate_typename", || generate_typename(&program, true));
    if let Some(print_stats) = maybe_print_stats {
        print_stats("generate_typename", &program);
    }

    log_event.time("flatten", || flatten(&mut program, true, false))?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("flatten", &program);
    }

    program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    if let Some(print_stats) = maybe_print_stats {
        print_stats("skip_redundant_nodes", &program);
    }

    program = log_event.time("hash_supported_argument", || {
        hash_supported_argument(&program, &feature_flags)
    })?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("hash_supported_argument", &program);
    }

    program = log_event.time("generate_test_operation_metadata", || {
        generate_test_operation_metadata(&program, &test_path_regex)
    })?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("generate_test_operation_metadata", &program);
    }

    log_event.complete();

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the printed operation text.
///
/// Corresponds to the "print transforms" in the JS compiler
fn apply_operation_text_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    feature_flags: Arc<FeatureFlags>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_operation_text_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("apply_fragment_arguments", || {
        apply_fragment_arguments(
            &program,
            false,
            &feature_flags.no_inline,
            &base_fragment_names,
        )
    })?;
    log_event.time("validate_global_variables", || {
        validate_global_variables(&program)
    })?;
    program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    program = log_event.time("skip_split_operation", || skip_split_operation(&program));
    program = log_event.time("skip_client_extensions", || {
        skip_client_extensions(&program)
    });
    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program))?;
    program = log_event.time("generate_typename", || generate_typename(&program, false));
    program = log_event.time("skip_null_arguments_transform", || {
        skip_null_arguments_transform(&program)
    });
    log_event.time("validate_selection_conflict", || {
        validate_selection_conflict(&program)
    })?;
    log_event.time("flatten", || flatten(&mut program, false, true))?;
    program = log_event.time("validate_operation_variables", || {
        validate_operation_variables(&program)
    })?;
    program = log_event.time("skip_client_directives", || {
        skip_client_directives(&program)
    });
    log_event.time("validate_required_arguments", || {
        validate_required_arguments(&program)
    })?;
    program = log_event.time("unwrap_custom_directive_selection", || {
        unwrap_custom_directive_selection(&program)
    });

    log_event.complete();

    Ok(Arc::new(program))
}

fn apply_typegen_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_typegen_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("mask", || mask(&program));
    program = log_event.time("transform_match", || {
        transform_match(&program, &feature_flags)
    })?;
    program = log_event.time("transform_subscriptions", || {
        transform_subscriptions(&program)
    })?;
    program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;
    program = log_event.time("relay_resolvers", || {
        relay_resolvers(&program, feature_flags.enable_relay_resolver_transform)
    })?;
    log_event.time("flatten", || flatten(&mut program, false, false))?;
    program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, true)
    })?;
    program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });

    program = log_event.time("relay_actor_change_transform", || {
        relay_actor_change_transform(&program, &feature_flags.actor_change_support)
    })?;

    log_event.complete();

    Ok(Arc::new(program))
}
<commit_msg>move hash supported_arguments earlier<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::match_::hash_supported_argument;

use super::*;
use common::{sync::try_join, DiagnosticsResult, PerfLogEvent, PerfLogger};
use fnv::FnvHashSet;
use graphql_ir::Program;
use interner::StringKey;
use regex::Regex;
use std::sync::Arc;

#[derive(Debug)]
pub struct Programs {
    pub source: Arc<Program>,
    pub reader: Arc<Program>,
    pub normalization: Arc<Program>,
    pub operation_text: Arc<Program>,
    pub typegen: Arc<Program>,
}

pub fn apply_transforms<TPerfLogger>(
    project_name: StringKey,
    program: Arc<Program>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    test_path_regex: &Option<Regex>,
    perf_logger: Arc<TPerfLogger>,
    print_stats: Option<fn(extra_info: &'static str, program: &Program) -> ()>,
) -> DiagnosticsResult<Programs>
where
    TPerfLogger: PerfLogger + 'static,
{
    // The execution pipeline is as follows, where items at the same indentation
    // can be computed independently and therefore in parallel:
    // |- common
    //    |- reader
    //    |- operation
    //       |- normalization
    //       |- operation_text
    // |- typegen
    //
    // NOTE: try_join(f1, f2) prefers the errors from f1 over f2, so process the normalization
    // program first since it is likely to include more errors (both per-fragment and
    // whole-operation errors) than the reader_program which just includes per-fragment errors.
    let (((normalization_program, text_program), reader_program), typegen_program) = try_join(
        || {
            let common_program = apply_common_transforms(
                project_name,
                Arc::clone(&program),
                connection_interface,
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )?;

            try_join(
                || {
                    let operation_program = apply_operation_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        connection_interface,
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )?;

                    try_join(
                        || {
                            apply_normalization_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&base_fragment_names),
                                Arc::clone(&feature_flags),
                                test_path_regex.clone(),
                                Arc::clone(&perf_logger),
                                print_stats,
                            )
                        },
                        || {
                            apply_operation_text_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&base_fragment_names),
                                Arc::clone(&feature_flags),
                                Arc::clone(&perf_logger),
                            )
                        },
                    )
                },
                || {
                    apply_reader_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        Arc::clone(&feature_flags),
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )
                },
            )
        },
        || {
            apply_typegen_transforms(
                project_name,
                Arc::clone(&program),
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )
        },
    )?;

    Ok(Programs {
        source: program,
        reader: reader_program,
        normalization: normalization_program,
        operation_text: text_program,
        typegen: typegen_program,
    })
}

/// Applies transforms that apply to every output.
fn apply_common_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_common_transforms");
    log_event.string("project", project_name.to_string());
    let mut program = log_event.time("transform_connections", || {
        transform_connections(&program, connection_interface)
    });
    program = log_event.time("mask", || mask(&program));
    program = log_event.time("transform_defer_stream", || {
        transform_defer_stream(&program)
    })?;
    program = log_event.time("transform_match", || {
        transform_match(&program, &feature_flags)
    })?;
    program = log_event.time("transform_subscriptions", || {
        transform_subscriptions(&program)
    })?;
    program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, false)
    })?;

    program = log_event.time("relay_resolvers", || {
        relay_resolvers(&program, feature_flags.enable_relay_resolver_transform)
    })?;

    if feature_flags.enable_flight_transform {
        program = log_event.time("react_flight", || react_flight(&program))?;
        program = log_event.time("relay_client_component", || {
            relay_client_component(&program, &feature_flags)
        })?;
    }

    program = log_event.time("relay_actor_change_transform", || {
        relay_actor_change_transform(&program, &feature_flags.actor_change_support)
    })?;

    log_event.complete();

    Ok(Arc::new(program))
}

/// Applies transforms only for generated reader code.
/// Corresponds to the "fragment transforms" in the JS compiler.
fn apply_reader_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_reader_transforms");
    log_event.string("project", project_name.to_string());
    let mut program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;

    program = log_event.time("client_extensions", || client_extensions(&program));
    program = log_event.time("handle_field_transform", || {
        handle_field_transform(&program)
    });
    program = log_event.time("inline_data_fragment", || inline_data_fragment(&program))?;
    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program))?;
    program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });
    log_event.time("flatten", || flatten(&mut program, true, false))?;
    program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    program = log_event.time("generate_data_driven_dependency_metadata", || {
        generate_data_driven_dependency_metadata(&program)
    });
    program = log_event.time("hash_supported_argument", || {
        hash_supported_argument(&program, &feature_flags)
    })?;

    log_event.complete();

    Ok(Arc::new(program))
}

/// Applies transforms that apply to all operation artifacts.
/// Corresponds to the "query transforms" in the JS compiler.
fn apply_operation_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_operation_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("split_module_import", || {
        split_module_import(&program, &base_fragment_names)
    });
    program = log_event.time("generate_id_field", || generate_id_field(&program));
    program = log_event.time("declarative_connection", || {
        transform_declarative_connection(&program, connection_interface)
    })?;

    // TODO(T67052528): execute FB-specific transforms only if config options is provided
    program = log_event.time("generate_subscription_name_metadata", || {
        generate_subscription_name_metadata(&program)
    })?;
    program = log_event.time("generate_live_query_metadata", || {
        generate_live_query_metadata(&program)
    })?;

    log_event.complete();

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the generated normalization code.
///
/// Corresponds to the "codegen transforms" in the JS compiler
fn apply_normalization_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    feature_flags: Arc<FeatureFlags>,
    test_path_regex: Option<Regex>,
    perf_logger: Arc<impl PerfLogger>,
    maybe_print_stats: Option<fn(extra_info: &'static str, program: &Program) -> ()>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_normalization_transforms");
    log_event.string("project", project_name.to_string());
    if let Some(print_stats) = maybe_print_stats {
        print_stats("normalization start", &program);
    }

    let mut program = log_event.time("apply_fragment_arguments", || {
        apply_fragment_arguments(
            &program,
            true,
            &feature_flags.no_inline,
            &base_fragment_names,
        )
    })?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("apply_fragment_arguments", &program);
    }

    program = log_event.time("hash_supported_argument", || {
        hash_supported_argument(&program, &feature_flags)
    })?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("hash_supported_argument", &program);
    }

    program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("relay_early_flush", &program);
    }

    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program))?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("skip_unreachable_node", &program);
    }

    program = log_event.time("inline_fragments", || inline_fragments(&program));
    if let Some(print_stats) = maybe_print_stats {
        print_stats("inline_fragments", &program);
    }

    program = log_event.time("client_extensions", || client_extensions(&program));
    if let Some(print_stats) = maybe_print_stats {
        print_stats("client_extensions", &program);
    }

    program = log_event.time("generate_typename", || generate_typename(&program, true));
    if let Some(print_stats) = maybe_print_stats {
        print_stats("generate_typename", &program);
    }

    log_event.time("flatten", || flatten(&mut program, true, false))?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("flatten", &program);
    }

    program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    if let Some(print_stats) = maybe_print_stats {
        print_stats("skip_redundant_nodes", &program);
    }

    program = log_event.time("generate_test_operation_metadata", || {
        generate_test_operation_metadata(&program, &test_path_regex)
    })?;
    if let Some(print_stats) = maybe_print_stats {
        print_stats("generate_test_operation_metadata", &program);
    }

    log_event.complete();

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the printed operation text.
///
/// Corresponds to the "print transforms" in the JS compiler
fn apply_operation_text_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    feature_flags: Arc<FeatureFlags>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_operation_text_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("apply_fragment_arguments", || {
        apply_fragment_arguments(
            &program,
            false,
            &feature_flags.no_inline,
            &base_fragment_names,
        )
    })?;
    log_event.time("validate_global_variables", || {
        validate_global_variables(&program)
    })?;
    program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    program = log_event.time("skip_split_operation", || skip_split_operation(&program));
    program = log_event.time("skip_client_extensions", || {
        skip_client_extensions(&program)
    });
    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program))?;
    program = log_event.time("generate_typename", || generate_typename(&program, false));
    program = log_event.time("skip_null_arguments_transform", || {
        skip_null_arguments_transform(&program)
    });
    log_event.time("validate_selection_conflict", || {
        validate_selection_conflict(&program)
    })?;
    log_event.time("flatten", || flatten(&mut program, false, true))?;
    program = log_event.time("validate_operation_variables", || {
        validate_operation_variables(&program)
    })?;
    program = log_event.time("skip_client_directives", || {
        skip_client_directives(&program)
    });
    log_event.time("validate_required_arguments", || {
        validate_required_arguments(&program)
    })?;
    program = log_event.time("unwrap_custom_directive_selection", || {
        unwrap_custom_directive_selection(&program)
    });

    log_event.complete();

    Ok(Arc::new(program))
}

fn apply_typegen_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_typegen_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("mask", || mask(&program));
    program = log_event.time("transform_match", || {
        transform_match(&program, &feature_flags)
    })?;
    program = log_event.time("transform_subscriptions", || {
        transform_subscriptions(&program)
    })?;
    program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;
    program = log_event.time("relay_resolvers", || {
        relay_resolvers(&program, feature_flags.enable_relay_resolver_transform)
    })?;
    log_event.time("flatten", || flatten(&mut program, false, false))?;
    program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, true)
    })?;
    program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });

    program = log_event.time("relay_actor_change_transform", || {
        relay_actor_change_transform(&program, &feature_flags.actor_change_support)
    })?;

    log_event.complete();

    Ok(Arc::new(program))
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::HTMLTableElementBinding;
use dom::bindings::codegen::Bindings::HTMLTableElementBinding::HTMLTableElementMethods;
use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, NodeCast, HTMLTableCaptionElementCast};
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::HTMLTableCaptionElementTypeId;
use dom::element::HTMLTableElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::htmltablecaptionelement::HTMLTableCaptionElement;
use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
use servo_util::str::DOMString;

#[dom_struct]
pub struct HTMLTableElement {
    htmlelement: HTMLElement,
}

impl HTMLTableElementDerived for EventTarget {
    fn is_htmltableelement(&self) -> bool {
        *self.type_id() == NodeTargetTypeId(ElementNodeTypeId(HTMLTableElementTypeId))
    }
}

impl HTMLTableElement {
    fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableElement {
        HTMLTableElement {
            htmlelement: HTMLElement::new_inherited(HTMLTableElementTypeId, localName, prefix, document)
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTableElement> {
        let element = HTMLTableElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLTableElementBinding::Wrap)
    }
}

impl Reflectable for HTMLTableElement {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.htmlelement.reflector()
    }
}

impl<'a> HTMLTableElementMethods for JSRef<'a, HTMLTableElement> {
    //  http://www.whatwg.org/html/#dom-table-caption
    fn GetCaption(self) -> Option<Temporary<HTMLTableCaptionElement>> {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        node.children().find(|child| {
            child.type_id() == ElementNodeTypeId(HTMLTableCaptionElementTypeId)
        }).map(|node| {
            Temporary::from_rooted(HTMLTableCaptionElementCast::to_ref(node).unwrap())
        })
    }

    // http://www.whatwg.org/html/#dom-table-caption
    fn SetCaption(self, new_caption: Option<JSRef<HTMLTableCaptionElement>>) {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        let old_caption = self.GetCaption();

        match old_caption {
            Some(htmlelem) => {
                let htmlelem_root = htmlelem.root();
                let old_caption_node: JSRef<Node> = NodeCast::from_ref(*htmlelem_root);
                assert!(node.RemoveChild(old_caption_node).is_ok());
            }
            None => ()
        }

        new_caption.map(|caption| {
            let new_caption_node: JSRef<Node> = NodeCast::from_ref(caption);
            assert!(node.AppendChild(new_caption_node).is_ok());
        });
    }
}
<commit_msg>Cleanup the implementation of HTMLTableElement::GetCaption.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::HTMLTableElementBinding;
use dom::bindings::codegen::Bindings::HTMLTableElementBinding::HTMLTableElementMethods;
use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, NodeCast, HTMLTableCaptionElementCast};
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::HTMLTableElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::htmltablecaptionelement::HTMLTableCaptionElement;
use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
use servo_util::str::DOMString;

#[dom_struct]
pub struct HTMLTableElement {
    htmlelement: HTMLElement,
}

impl HTMLTableElementDerived for EventTarget {
    fn is_htmltableelement(&self) -> bool {
        *self.type_id() == NodeTargetTypeId(ElementNodeTypeId(HTMLTableElementTypeId))
    }
}

impl HTMLTableElement {
    fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableElement {
        HTMLTableElement {
            htmlelement: HTMLElement::new_inherited(HTMLTableElementTypeId, localName, prefix, document)
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTableElement> {
        let element = HTMLTableElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLTableElementBinding::Wrap)
    }
}

impl Reflectable for HTMLTableElement {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.htmlelement.reflector()
    }
}

impl<'a> HTMLTableElementMethods for JSRef<'a, HTMLTableElement> {
    //  http://www.whatwg.org/html/#dom-table-caption
    fn GetCaption(self) -> Option<Temporary<HTMLTableCaptionElement>> {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        node.children()
            .filter_map::<JSRef<HTMLTableCaptionElement>>(HTMLTableCaptionElementCast::to_ref)
            .next()
            .map(Temporary::from_rooted)
    }

    // http://www.whatwg.org/html/#dom-table-caption
    fn SetCaption(self, new_caption: Option<JSRef<HTMLTableCaptionElement>>) {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        let old_caption = self.GetCaption();

        match old_caption {
            Some(htmlelem) => {
                let htmlelem_root = htmlelem.root();
                let old_caption_node: JSRef<Node> = NodeCast::from_ref(*htmlelem_root);
                assert!(node.RemoveChild(old_caption_node).is_ok());
            }
            None => ()
        }

        new_caption.map(|caption| {
            let new_caption_node: JSRef<Node> = NodeCast::from_ref(caption);
            assert!(node.AppendChild(new_caption_node).is_ok());
        });
    }
}
<|endoftext|>