blob: bb5e92b78cfdd05f6d7b4fb42bfe2a6b5696af5f [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define C_LUCY_OBJ
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "Lucy/Object/Obj.h"
#include "Lucy/Object/CharBuf.h"
#include "Lucy/Object/Err.h"
#include "Lucy/Object/VTable.h"
#include "Lucy/Util/Memory.h"
static void
S_lazy_init_host_obj(lucy_Obj *self) {
SV *inner_obj = newSV(0);
SvOBJECT_on(inner_obj);
PL_sv_objcount++;
SvUPGRADE(inner_obj, SVt_PVMG);
sv_setiv(inner_obj, PTR2IV(self));
// Connect class association.
lucy_CharBuf *class_name = Lucy_VTable_Get_Name(self->vtable);
HV *stash = gv_stashpvn((char*)Lucy_CB_Get_Ptr8(class_name),
Lucy_CB_Get_Size(class_name), TRUE);
SvSTASH_set(inner_obj, (HV*)SvREFCNT_inc(stash));
/* Up till now we've been keeping track of the refcount in
* self->ref.count. We're replacing ref.count with ref.host_obj, which
* will assume responsibility for maintaining the refcount. ref.host_obj
* starts off with a refcount of 1, so we need to transfer any refcounts
* in excess of that. */
size_t old_refcount = self->ref.count;
self->ref.host_obj = inner_obj;
while (old_refcount > 1) {
SvREFCNT_inc_simple_void_NN(inner_obj);
old_refcount--;
}
}
uint32_t
lucy_Obj_get_refcount(lucy_Obj *self) {
return self->ref.count < 4
? self->ref.count
: SvREFCNT((SV*)self->ref.host_obj);
}
lucy_Obj*
lucy_Obj_inc_refcount(lucy_Obj *self) {
switch (self->ref.count) {
case 0:
CFISH_THROW(LUCY_ERR, "Illegal refcount of 0");
break; // useless
case 1:
case 2:
self->ref.count++;
break;
case 3:
S_lazy_init_host_obj(self);
// fall through
default:
SvREFCNT_inc_simple_void_NN((SV*)self->ref.host_obj);
}
return self;
}
uint32_t
lucy_Obj_dec_refcount(lucy_Obj *self) {
uint32_t modified_refcount = I32_MAX;
switch (self->ref.count) {
case 0:
CFISH_THROW(LUCY_ERR, "Illegal refcount of 0");
break; // useless
case 1:
modified_refcount = 0;
Lucy_Obj_Destroy(self);
break;
case 2:
case 3:
modified_refcount = --self->ref.count;
break;
default:
modified_refcount = SvREFCNT((SV*)self->ref.host_obj) - 1;
// If the SV's refcount falls to 0, DESTROY will be invoked from
// Perl-space.
SvREFCNT_dec((SV*)self->ref.host_obj);
}
return modified_refcount;
}
void*
lucy_Obj_to_host(lucy_Obj *self) {
if (self->ref.count < 4) { S_lazy_init_host_obj(self); }
return newRV_inc((SV*)self->ref.host_obj);
}