<commit_before>use std::ptr;

use baseline::stub::ensure_stub;
use class::{self, ClassDef, ClassDefId, ClassSize};
use ctxt::SemContext;
use mem;
use object::Header;
use vtable::{DISPLAY_SIZE, VTableBox};
use ty::BuiltinType;

pub fn specialize_type<'ast>(ctxt: &SemContext<'ast>,
                             ty: BuiltinType,
                             specialize_for: SpecializeFor,
                             type_params: &[BuiltinType])
                             -> BuiltinType {
    match ty {
        BuiltinType::ClassTypeParam(_, id) => {
            if specialize_for == SpecializeFor::Class {
                type_params[id.idx()]
            } else {
                ty
            }
        }

        BuiltinType::FctTypeParam(_, id) => {
            if specialize_for == SpecializeFor::Fct {
                type_params[id.idx()]
            } else {
                ty
            }
        }

        BuiltinType::Generic(type_id) => {
            let ty = ctxt.types.borrow().get(type_id);

            let params: Vec<_> = ty.params
                .iter()
                .map(|&t| specialize_type(ctxt, t, specialize_for, type_params))
                .collect();

            let type_id = ctxt.types.borrow_mut().insert(ty.cls_id, params);

            BuiltinType::Generic(type_id)
        }

        _ => ty,
    }
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum SpecializeFor {
    Fct,
    Class,
}

pub fn specialize_class_def(ctxt: &SemContext,
                            cls: &class::Class,
                            type_params: &[BuiltinType])
                            -> ClassDefId {
    if let Some(&id) = cls.specializations.borrow().get(type_params) {
        return id;
    }

    create_specialized_class_def(ctxt, cls, type_params)
}

fn create_specialized_class_def(ctxt: &SemContext,
                                cls: &class::Class,
                                type_params: &[BuiltinType])
                                -> ClassDefId {
    let id: ClassDefId = ctxt.class_defs.len().into();

    let old = cls.specializations.borrow_mut().insert(type_params.to_vec(), id);
    assert!(old.is_none());

    ctxt.class_defs
        .push(ClassDef {
                  id: id,
                  cls_id: cls.id,
                  type_params: type_params.to_vec(),
                  parent_id: None,
                  size: ClassSize::Fixed(0),
                  fields: Vec::new(),
                  ref_fields: Vec::new(),
                  vtable: None,
              });

    let mut fields;
    let mut ref_fields;
    let mut size;
    let mut vtable_entries = Vec::new();
    let parent_id;

    if cls.is_array || cls.is_str {
        fields = Vec::new();
        ref_fields = Vec::new();
        size = 0;

        let super_id = cls.parent_class.expect("Array & Str should have super class");
        let sup = ctxt.classes[super_id].borrow();
        let id = specialize_class_def(ctxt, &*sup, &[]);
        parent_id = Some(id);

    } else {
        if let Some(super_id) = cls.parent_class {
            let sup = ctxt.classes[super_id].borrow();
            let id = specialize_class_def(ctxt, &*sup, &[]);
            let cls_def = ctxt.class_defs[id].borrow();

            fields = cls_def.fields.clone();
            ref_fields = cls_def.ref_fields.clone();
            size = match cls_def.size {
                ClassSize::Fixed(size) => size,
                _ => unreachable!(),
            };
            parent_id = Some(id);

            let vtable = sup.vtable.as_ref().unwrap();
            vtable_entries.extend_from_slice(vtable.table());

        } else {
            fields = Vec::with_capacity(cls.fields.len());
            ref_fields = Vec::new();
            size = Header::size();
            parent_id = None;
        };

        for f in &cls.fields {
            let ty = specialize_type(ctxt, f.ty, SpecializeFor::Class, &type_params);
            debug_assert!(!ty.contains_type_param(ctxt));

            let field_size = f.ty.size(ctxt);
            let field_align = f.ty.align(ctxt);

            let offset = mem::align_i32(size, field_align);
            fields.push(offset);

            size = offset + field_size;

            if ty.reference_type() {
                ref_fields.push(offset);
            }
        }

        size = mem::align_i32(size, mem::ptr_width());
    }

    for &mid in &cls.methods {
        let mut fct = ctxt.fcts[mid].borrow_mut();

        if fct.vtable_index.is_some() {
            continue;
        }

        if fct.is_virtual() {
            let vtable_index = if let Some(overrides) = fct.overrides {
                ctxt.fcts[overrides].borrow().vtable_index.unwrap()

            } else {
                let vtable_index = vtable_entries.len();
                vtable_entries.push(ensure_stub(ctxt) as usize);

                vtable_index as u32
            };

            fct.vtable_index = Some(vtable_index);
        }
    }

    let mut cls_def = ctxt.class_defs[id].borrow_mut();
    cls_def.size = ClassSize::Fixed(size);
    cls_def.fields = fields;
    cls_def.ref_fields = ref_fields;
    cls_def.parent_id = parent_id;

    let clsptr = (&*cls_def) as *const class::ClassDef as *mut class::ClassDef;
    let vtable = VTableBox::new(clsptr, &vtable_entries);
    cls_def.vtable = Some(vtable);

    ensure_display(ctxt, &mut cls_def);

    id<commit_msg>store dynamic class size for Arrray and Str<commit_after>use std::ptr;

use baseline::stub::ensure_stub;
use class::{self, ClassDef, ClassDefId, ClassSize};
use ctxt::SemContext;
use mem;
use object::Header;
use vtable::{DISPLAY_SIZE, VTableBox};
use ty::BuiltinType;

pub fn specialize_type<'ast>(ctxt: &SemContext<'ast>,
                             ty: BuiltinType,
                             specialize_for: SpecializeFor,
                             type_params: &[BuiltinType])
                             -> BuiltinType {
    match ty {
        BuiltinType::ClassTypeParam(_, id) => {
            if specialize_for == SpecializeFor::Class {
                type_params[id.idx()]
            } else {
                ty
            }
        }

        BuiltinType::FctTypeParam(_, id) => {
            if specialize_for == SpecializeFor::Fct {
                type_params[id.idx()]
            } else {
                ty
            }
        }

        BuiltinType::Generic(type_id) => {
            let ty = ctxt.types.borrow().get(type_id);

            let params: Vec<_> = ty.params
                .iter()
                .map(|&t| specialize_type(ctxt, t, specialize_for, type_params))
                .collect();

            let type_id = ctxt.types.borrow_mut().insert(ty.cls_id, params);

            BuiltinType::Generic(type_id)
        }

        _ => ty,
    }
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum SpecializeFor {
    Fct,
    Class,
}

pub fn specialize_class_def(ctxt: &SemContext,
                            cls: &class::Class,
                            type_params: &[BuiltinType])
                            -> ClassDefId {
    if let Some(&id) = cls.specializations.borrow().get(type_params) {
        return id;
    }

    create_specialized_class_def(ctxt, cls, type_params)
}

fn create_specialized_class_def(ctxt: &SemContext,
                                cls: &class::Class,
                                type_params: &[BuiltinType])
                                -> ClassDefId {
    let id: ClassDefId = ctxt.class_defs.len().into();

    let old = cls.specializations.borrow_mut().insert(type_params.to_vec(), id);
    assert!(old.is_none());

    ctxt.class_defs
        .push(ClassDef {
                  id: id,
                  cls_id: cls.id,
                  type_params: type_params.to_vec(),
                  parent_id: None,
                  size: ClassSize::Fixed(0),
                  fields: Vec::new(),
                  ref_fields: Vec::new(),
                  vtable: None,
              });

    let mut fields;
    let mut ref_fields;
    let size;
    let mut vtable_entries = Vec::new();
    let parent_id;

    if cls.is_array || cls.is_str {
        fields = Vec::new();
        ref_fields = Vec::new();

        size = if cls.is_array && type_params[0].reference_type() {
            ClassSize::ObjArray
        } else if cls.is_array {
            ClassSize::Array(type_params[0].size(ctxt))
        } else {
            ClassSize::Str
        };

        let super_id = cls.parent_class.expect("Array & Str should have super class");
        let sup = ctxt.classes[super_id].borrow();
        let id = specialize_class_def(ctxt, &*sup, &[]);
        parent_id = Some(id);

    } else {
        let mut csize;

        if let Some(super_id) = cls.parent_class {
            let sup = ctxt.classes[super_id].borrow();
            let id = specialize_class_def(ctxt, &*sup, &[]);
            let cls_def = ctxt.class_defs[id].borrow();

            fields = cls_def.fields.clone();
            ref_fields = cls_def.ref_fields.clone();
            csize = match cls_def.size {
                ClassSize::Fixed(size) => size,
                _ => unreachable!(),
            };
            parent_id = Some(id);

            let vtable = sup.vtable.as_ref().unwrap();
            vtable_entries.extend_from_slice(vtable.table());

        } else {
            fields = Vec::with_capacity(cls.fields.len());
            ref_fields = Vec::new();
            csize = Header::size();
            parent_id = None;
        };

        for f in &cls.fields {
            let ty = specialize_type(ctxt, f.ty, SpecializeFor::Class, &type_params);
            debug_assert!(!ty.contains_type_param(ctxt));

            let field_size = f.ty.size(ctxt);
            let field_align = f.ty.align(ctxt);

            let offset = mem::align_i32(csize, field_align);
            fields.push(offset);

            csize = offset + field_size;

            if ty.reference_type() {
                ref_fields.push(offset);
            }
        }

        size = ClassSize::Fixed(mem::align_i32(csize, mem::ptr_width()));
    }

    for &mid in &cls.methods {
        let mut fct = ctxt.fcts[mid].borrow_mut();

        if fct.vtable_index.is_some() {
            continue;
        }

        if fct.is_virtual() {
            let vtable_index = if let Some(overrides) = fct.overrides {
                ctxt.fcts[overrides].borrow().vtable_index.unwrap()

            } else {
                let vtable_index = vtable_entries.len();
                vtable_entries.push(ensure_stub(ctxt) as usize);

                vtable_index as u32
            };

            fct.vtable_index = Some(vtable_index);
        }
    }

    let mut cls_def = ctxt.class_defs[id].borrow_mut();
    cls_def.size = size;
    cls_def.fields = fields;
    cls_def.ref_fields = ref_fields;
    cls_def.parent_id = parent_id;

    let clsptr = (&*cls_def) as *const class::ClassDef as *mut class::ClassDef;
    let vtable = VTableBox::new(clsptr, &vtable_entries);
    cls_def.vtable = Some(vtable);

    ensure_display(ctxt, &mut cls_def);

    id
{'fraction_non_alphanumeric': 0.1203203743700504, 'fraction_numerical': 0.0015298776097912167, 'mean_word_length': 1.6881954523463958, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7356377', 'n_tokens_mistral': 3448, 'n_tokens_neox': 3412, 'n_words': 631}