mirror of
https://github.com/darlinghq/darling-libobjc2.git
synced 2025-01-01 09:18:29 +00:00
221 lines
5.6 KiB
Objective-C
221 lines
5.6 KiB
Objective-C
#include "../objc/runtime.h"
|
|
#import "malloc.h"
|
|
#import "object.h"
|
|
#import "thread.h"
|
|
#import "trace.h"
|
|
#import "cycle.h"
|
|
#import "visit.h"
|
|
#import "workqueue.h"
|
|
#import "static.h"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
/**
|
|
* Pointer comparison. Needed for the hash table.
|
|
*/
|
|
static int pointer_compare(const void *a, const void *b)
|
|
{
|
|
return a == b;
|
|
}
|
|
static int pointer_hash(const void *obj)
|
|
{
|
|
intptr_t ptr = (intptr_t)obj;
|
|
return (ptr >> 8) | (ptr << 8);
|
|
}
|
|
#define MAP_TABLE_NAME known_object
|
|
#define MAP_TABLE_COMPARE_FUNCTION pointer_compare
|
|
#define MAP_TABLE_HASH_KEY pointer_hash
|
|
#define MAP_TABLE_HASH_VALUE pointer_hash
|
|
#include "../hash_table.h"
|
|
|
|
@interface GCObject
|
|
- (void)finalize;
|
|
@end
|
|
|
|
static void* malloc_zone_alloc(void *zone, size_t bytes)
|
|
{
|
|
return calloc(1, bytes);
|
|
}
|
|
|
|
void *(*gc_alloc_with_zone)(void *zone, size_t bytes) = malloc_zone_alloc;
|
|
|
|
static void malloc_zone_free(void *zone, void *mem)
|
|
{
|
|
free(mem);
|
|
}
|
|
|
|
void (*gc_free_with_zone)(void *zone, void *mem) = malloc_zone_free;
|
|
|
|
/**
|
|
* Macro for calculating the size of a header structure, including padding
|
|
* required for alignment.
|
|
*/
|
|
#define headerSize(header)\
|
|
({\
|
|
size_t headerSize = sizeof(struct header);\
|
|
/* Everything else expects the isa pointer to be correctly aligned and all\
|
|
* subsequent ivars will be placed with the assumption that they have the\
|
|
* correct alignment, so make sure this is really the case. */\
|
|
if (headerSize % __alignof(void*))\
|
|
{\
|
|
headerSize += headerSize % __alignof(void*);\
|
|
}\
|
|
headerSize;\
|
|
})
|
|
|
|
id GCAllocateObjectWithZone(Class cls, void *zone, size_t extraBytes)
|
|
{
|
|
// Allocate space for the header and ivars.
|
|
size_t allocSize = headerSize(gc_object_header) + class_getInstanceSize(cls);
|
|
// And for the extra space that we were asked for.
|
|
allocSize += extraBytes;
|
|
struct gc_object_header *region = gc_alloc_with_zone(zone, allocSize);
|
|
region->zone = zone;
|
|
id obj = (id)((char*)region + headerSize(gc_object_header));
|
|
obj->isa = cls;
|
|
// Reference count is 0, so set visited to prevent it from being collected
|
|
// immediately
|
|
GCSetFlag(obj, GCFlagVisited);
|
|
// Mark as free or in use.
|
|
GCSetColourOfObject(obj, GCColourBlack);
|
|
// Add to traced map later, if it hasn't been retained
|
|
GCAddObject(obj);
|
|
return obj;
|
|
}
|
|
|
|
void *GCAllocateBufferWithZone(void *zone, size_t size, BOOL scan)
|
|
{
|
|
size_t allocSize = headerSize(gc_buffer_header) + size;
|
|
struct gc_buffer_header *region = gc_alloc_with_zone(zone, allocSize);
|
|
region->size = size;
|
|
region->object_header.zone = zone;
|
|
char *buffer = ((char*)region) + headerSize(gc_buffer_header);
|
|
if (scan)
|
|
{
|
|
GCAddBufferForTracing(region);
|
|
}
|
|
// Reference count is 0, so set visited to prevent it from being collected
|
|
// immediately
|
|
GCSetFlag((id)buffer, GCFlagVisited);
|
|
GCSetFlag((id)buffer, GCFlagNotObject);
|
|
// Mark as free or in use.
|
|
GCSetColourOfObject((id)buffer, GCColourBlack);
|
|
// Add to traced map later, if it hasn't been retained
|
|
GCAddObject((id)buffer);
|
|
return buffer;
|
|
}
|
|
|
|
static void freeObject(id object)
|
|
{
|
|
void * addr;
|
|
if (GCTestFlag(object, GCFlagNotObject))
|
|
{
|
|
fprintf(stderr, "Freeing bufer %x\n", (int)object);
|
|
addr = GCHeaderForBuffer(object);
|
|
}
|
|
else
|
|
{
|
|
addr = GCHeaderForObject(object);
|
|
}
|
|
fprintf(stderr, "Freeing %x\n", (int)object);
|
|
gc_free_with_zone(GCHeaderForObject(object)->zone, addr);
|
|
}
|
|
|
|
void GCWeakRelease(id anObject)
|
|
{
|
|
if (!GCObjectIsDynamic(anObject)) { return; }
|
|
long count = GCDecrementWeakCount(anObject);
|
|
// If the object has been finalized and this is the last weak ref, free it.
|
|
if (count == 0 && GCColourOfObject(anObject) == GCColourOrange)
|
|
{
|
|
freeObject(anObject);
|
|
}
|
|
}
|
|
id GCWeakRetain(id anObject)
|
|
{
|
|
if (!GCObjectIsDynamic(anObject)) { return anObject; }
|
|
// If this object has already been finalized, return nil.
|
|
if (GCColourOfObject(anObject) == GCColourOrange)
|
|
{
|
|
return nil;
|
|
}
|
|
GCIncrementWeakCount(anObject);
|
|
return anObject;
|
|
}
|
|
// NOTE: Weak read should add the object for tracing.
|
|
|
|
void GCAddObjectForTracing(id object);
|
|
|
|
static BOOL foundRedObjects;
|
|
|
|
static void releaseObjects(id object, void *context, BOOL isWeak)
|
|
{
|
|
//fprintf(stderr, "Releasing %x\n", (int)object);
|
|
if (isWeak)
|
|
{
|
|
GCWeakRelease(object);
|
|
}
|
|
else
|
|
{
|
|
GCColour colour = GCColourOfObject(object);
|
|
// If we're freeing a cycle, mark this object as orange, finalize it,
|
|
// then tell the tracing code to really delete it later
|
|
if (colour == GCColourRed)
|
|
{
|
|
foundRedObjects = YES;
|
|
GCSetColourOfObject(object, GCColourOrange);
|
|
[object finalize];
|
|
}
|
|
else if (colour != GCColourOrange)
|
|
{
|
|
GCRelease(object);
|
|
}
|
|
//fprintf(stderr, "Object has refcount %d\n", (int)GCGetRetainCount(object));
|
|
if (GCGetRetainCount(object) <= 0)
|
|
{
|
|
GCAddObjectForTracing(object);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finalizes an object and frees it if its weak reference count is 0.
|
|
*
|
|
* This version must only be called from the GC thread.
|
|
*/
|
|
void GCFreeObjectUnsafe(id object)
|
|
{
|
|
if (!GCObjectIsDynamic(object)) { return; }
|
|
|
|
//fprintf(stderr, "Going to Free object %x\n", (int)(object));
|
|
|
|
foundRedObjects = NO;
|
|
if (GCColourOrange != GCSetColourOfObject(object, GCColourOrange))
|
|
{
|
|
// If this is really an object, kill all of its references and then
|
|
// finalize it.
|
|
if (!GCTestFlag(object, GCFlagNotObject))
|
|
{
|
|
if (0)
|
|
GCVisitChildren(object, releaseObjects, NULL, YES);
|
|
[object finalize];
|
|
}
|
|
// FIXME: Implement this.
|
|
//GCRemoveRegionFromTracingUnsafe(region);
|
|
}
|
|
if (GCGetWeakRefCount(object) == 0)
|
|
{
|
|
//fprintf(stderr, "Freeing object %x\n", (int)(object));
|
|
freeObject(object);
|
|
}
|
|
if (foundRedObjects)
|
|
{
|
|
GCRunTracerIfNeeded(NO);
|
|
}
|
|
}
|
|
|
|
void GCFreeObject(id object)
|
|
{
|
|
GCPerform((void(*)(void*))GCFreeObjectUnsafe, object);
|
|
}
|