
fn property_update(on_object: ptr, property_ref: ptr, new_value: ?ptr) {
    let data = (@offset(on_object, u8, -8)).@cast(GcData)
    // let state = data.state
    if data.is_not_new() {
        let old = @ptrv(property_ref, ptr)
        if old & 1 == 1 {
            let old_item : ptr = old - 1
            let data = (@offset(old_item, u8, -8)).@cast(GcData)
            data.set_no_owner()
            gc.dis_owned_list.add_ptr(old_item)
        } else if old & 2 == 2 {
            let old_item : ptr = old - 2
            let data = (@offset(old_item, u8, -8)).@cast(GcData)
            data.dis_co_own()
        }

        // New value
        if isset(new_value) {
            if data.is_shared() {
                share(new_value)
            } else if data.is_owned() {
                if !data.is_in_updates() {
                    data.set_in_updates()
                    gc.updated_list.add_ptr(on_object)
                }
            }
        }
    }
    @ptrv(property_ref, ?ptr) = new_value
}

fn property_set(on_object: ptr, property_ref: ptr, value: ?ptr) {
    if isset(value) {
        let data = (@offset(on_object, u8, -8)).@cast(GcData)
        // let state = data.state
        if data.is_shared() {
            share(value)
        } else if data.is_owned() {
            if !data.is_in_updates() {
                data.set_in_updates()
                gc.updated_list.add_ptr(on_object)
            }
        }
    }
    @ptrv(property_ref, ?ptr) = value
}

fn property_get(property_ref: ptr) ptr {
    return @ptrv(property_ref, ?ptr) & -4.to(uint)
}

fn property_remove(on_object: ptr, property_ref: ptr) {
    let value = @ptrv(property_ref, ptr)
    if value & 1 == 1 {
        let item : ptr = value - 1
        let data = (@offset(item, u8, -8)).@cast(GcData)
        data.set_no_owner()
        gc.dis_owned_list.add_ptr(item)
    } else if value & 2 == 2 {
        let item : ptr = value - 2
        let data = (@offset(item, u8, -8)).@cast(GcData)
        data.dis_co_own()
    }
}

fn property_mark(on_object: GcItem, property_ref: ptr) {
    let value = @ptrv(property_ref, ?GcItem)
    if isset(value) && (value & 3 == 0) {
        let value_data = gcdata(value)
        if value_data.is_not_owned_not_shared() {
            @ptrv(property_ref, ptr) = value | 1
            // if value_data.state == state_new : increase_block(value)
            // value_data.state = state_owned
            mark_used(value)
        } else {
            // Co own
            @ptrv(property_ref, ptr) = value | 2
            value_data.co_own()
        }
    }
}

fn property_dis_own(on_object: ptr, property_ref: ptr) {
    let sub = @ptrv(property_ref, GcItem)
    if sub & 1 == 1 {
        // Remove ownership
        sub = sub - 1
        @ptrv(property_ref, GcItem) = sub
        let data = gcdata(sub)
        data.set_no_owner()
        if data.is_shared() : return
        dis_own_rec(sub)
    } else if sub & 2 == 2 {
        sub = sub - 2
        @ptrv(property_ref, GcItem) = sub
        let data = gcdata(sub)
        data.dis_co_own()
    }
}

fn property_share(on_object: ptr, property_ref: ptr) {
    let sub = @ptrv(property_ref, ?GcItem)
    if !isset(sub) : return
    let sub_item : GcItem = sub & -4.to(uint)
    let sub_data = gcdata(sub_item)
    if sub & 1 == 1 {
        // Remove ownership
        @ptrv(property_ref, GcItem) = sub_item
        sub_data.set_no_owner()
    } else if sub & 2 == 2 {
        // Remove co ownership
        @ptrv(property_ref, GcItem) = sub_item
        sub_data.dis_co_own()
    }
    share(sub_item)
}

fn property_update_mark(on_object: ptr, property_ref: ptr) {
    let value = @ptrv(property_ref, ?GcItem)
    if !isset(value) : return
    let item : GcItem = value & -4.to(uint)
    // if !gcdata(item).is_shared() : panic("Shared item has non shared properties (2)")
    share(item)
}

fn property_add_mark_list(on_object: ptr, property_ref: ptr) {
    let value = @ptrv(property_ref, ?GcItem)
    if !isset(value) : return
    let item : GcItem = value & -4.to(uint)
    let ml = gc.mark_list
    ml.add(item)
}
