Skip to content

Exported object not being retained by the context #2

@rraallvv

Description

@rraallvv

I'm trying to dynamically export the all the methods within a class to Lua like this

Class class = [NSProgress class];

// Create a protocol that inherits from JSExport and with all the public methods and properties of the class
const char *protocolName = class_getName(class);
Protocol *protocol = objc_allocateProtocol(protocolName);
protocol_addProtocol(protocol, objc_getProtocol("LuaExport"));

// Add the public methods of the class to the protocol
unsigned int methodCount, classMethodCount, propertyCount;
Method *methods, *classMethods;
objc_property_t *properties;

methods = class_copyMethodList(class, &methodCount);
for (unsigned int  methodIndex = 0; methodIndex < methodCount; ++methodIndex) {
    Method method = methods[methodIndex];
    protocol_addMethodDescription(protocol, method_getName(method), method_getTypeEncoding(method), YES, YES);
}

classMethods = class_copyMethodList(object_getClass(class), &classMethodCount);
for (unsigned int  methodIndex = 0; methodIndex < classMethodCount; ++methodIndex) {
    Method method = classMethods[methodIndex];
    protocol_addMethodDescription(protocol, method_getName(method), method_getTypeEncoding(method), YES, NO);
}

properties = class_copyPropertyList(class, &propertyCount);
for (unsigned int propertyIndex = 0; propertyIndex < propertyCount; ++propertyIndex) {
    objc_property_t property = properties[propertyIndex];

    unsigned int attributeCount;
    objc_property_attribute_t *attributes = property_copyAttributeList(property, &attributeCount);
    protocol_addProperty(protocol, property_getName(property), attributes, attributeCount, YES, YES);
    free(attributes);
}

free(methods);
free(classMethods);
free(properties);

// Add the new protocol to the class
objc_registerProtocol(protocol);
class_addProtocol(class, protocol);


LuaContext *ctx = [LuaContext new];
ctx[@"ex"] = [NSProgress progressWithTotalUnitCount:123];


NSError *error = nil;
[ctx parse:@"print(ex.totalUnitCount)" error:&error];
XCTAssert( ! error, @"failed to load script: %@", error);

The problem is that the exported object is immediately released so that when the script is parsed an EXC_BAD_ACCESS error is thrown at random places where the the object is get back from the wrapper

/* ... */
id obj = (__bridge id)wrapper->instance;
/* ... */

By retaining the the object changing line 328 to wrapper->instance = (__bridge_retained void*)object; the snippet above runs ok, but there is the problem of when the object must be released.

My suggestion is to make the wrapper an object instead of a struct, that holds the reference to the user data, like its counterpart JSValue

/* store the object's user data */
_index = luaL_ref(state, LUA_REGISTRYINDEX);

/* retrieve the object */
lua_rawgeti(state, LUA_REGISTRYINDEX, _index);

/* dispose of the  object's user data */
luaL_unref(state, LUA_REGISTRYINDEX, _index);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions