view dstep/objc/bridge/ClassInitializer.d @ 16:19885b43130e

Huge update, the bridge actually works now
author Jacob Carlborg <doob@me.com>
date Sun, 03 Jan 2010 22:06:11 +0100
parents 9fd439a28ce3
children
line wrap: on
line source

/**
 * Copyright: Copyright (c) 2009 Jacob Carlborg.
 * Authors: Jacob Carlborg
 * Version: Initial created: Apr 7, 2009
 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
 */
module dstep.objc.bridge.ClassInitializer;

version (Tango)
	import tango.math.Math;

else
	import std.math;

import dstep.internal.String;
import dstep.internal.Traits;
import bindings = dstep.objc.bindings;
import dstep.objc.bridge.Bridge;
import dstep.objc.bridge.TypeEncoding;
import dstep.objc.objc;
import dstep.objc.runtime;

/**
 * Objective-C subclass initializer template.
 * 
 * Param:
 * 	   subclassName = the name of the subclass to create
 * 	   superClassName = the name of the Objective-C superclass 
 */
package template ObjcSubclassInitializer (string subclassName, string superclassName)
{
	/**
	 * Return a pointer to the created class data structure. If this class hasn't been added
	 * to the Objective-C runtime, it'll be added.
	 *  
	 * Returns: a pointer to the created class data structure
	 */
	static dstep.objc.objc.Class objcClass ()
	{		
		if (__objcClass)
			return __objcClass;
		
		dstep.objc.objc.Class superClass = objcSuperClass;
		
		auto methods = __collectObjcInstanceMethods();
		auto classMethods = __collectObjcClassMethods();
		
		static if (dstep.internal.Traits.hasClassMethod!(typeof(super), objcClass.stringof))
			typeof(super).objcClass;
		
		return __objcClass = dstep.objc.bridge.ClassInitializer.subclassInit!(subclassName)(superClass, methods, classMethods);
	}
	
	/**
	 * Return a pointer to the superclass data structure. If the superclass hasn't been added
	 * to the Objective-C runtime, it'll be added.
	 * 
	 * Returns: a pointer to the superclass data structure
	 */
	static dstep.objc.objc.Class objcSuperClass ()
	{
		if (__objcSuperClass)
			return __objcSuperClass;
		
		if (is(typeof(super) == dstep.objc.bridge.Wrapper.ObjcWrapper)) // root class
			return __objcSuperClass = dstep.objc.bridge.Capsule.capsuleClass;
		
		__objcSuperClass = cast(dstep.objc.objc.Class) dstep.objc.objc.objc.getClass!(subclassName);
		
		while (!__objcSuperClass && !is(typeof(super) == dstep.objc.bridge.Wrapper.ObjcWrapper))
			__objcSuperClass = super.objcClass;
		
		if (!__objcSuperClass)
			__objcSuperClass = dstep.objc.bridge.Capsule.capsuleClass;
		
		return __objcSuperClass;
	}
	
	/**
	 * Collects all binded methods in the class this template is mixed into.
	 * 
	 * Returns: an array of methods
	 */
	private static dstep.objc.runtime.Method[] __collectObjcInstanceMethods ()
	{
		dstep.objc.runtime.Method[] methods;
		
		mixin("alias " ~ subclassName ~ " Type;");
				
		static if (Type.tupleof.length > 0)
		{
			foreach (i, f ; typeof(Type.tupleof))
			{
				const len = Type.stringof.length;
				const fieldName = Type.tupleof[i].stringof[1 + len + 2 .. $];			
				
				static if (fieldName == dstep.objc.bridge.Bridge.Bridge.objcMethodDeclarationVar)
				{				
					typeof(Type.tupleof[i]) field;

					dstep.objc.runtime.Method m = new dstep.objc.objc.objc_method;
					m.method_name = dstep.objc.objc.sel.registerName!(field.methodName);
					m.method_types = dstep.objc.bridge.TypeEncoding.encode!(field.returnType, dstep.objc.objc.id, dstep.objc.objc.SEL, field.argsType).ptr;
					m.method_imp = cast(dstep.objc.objc.IMP) field.methodImp;
					
					methods ~= m;
				}
			}
		}
		
		return methods;
	}
	
	/**
	 * Collects all binded class methods in the class this template is mixed into.
	 * 
	 * Returns:
	 */
	private static dstep.objc.runtime.Method[] __collectObjcClassMethods ()
	{
		dstep.objc.runtime.Method[] methods;
		
		mixin("alias " ~ subclassName ~ " Type;");
		
		static if (Type.tupleof.length > 0)
		{
			foreach (i, f ; typeof(Type.tupleof))
			{
				const len = Type.stringof.length;
				const fieldName = Type.tupleof[i].stringof[1 + len + 2 .. $];
				
				static if (fieldName == dstep.objc.bridge.Bridge.Bridge.objcClassMethodDeclarationVar)
				{				
					typeof(Type.tupleof[i]) field;

					dstep.objc.runtime.Method m = new dstep.objc.objc.objc_method;
					m.method_name = dstep.objc.objc.sel.registerName!(field.methodName);
					m.method_types = dstep.objc.bridge.TypeEncoding.encode!(field.returnType, field.argsType).ptr;
					m.method_imp = cast(dstep.objc.objc.IMP) field.methodImp;

					methods ~= m;
				}
			}
		}
		
		return methods;
	}
	
	/**
	 * Invoke on the receiver the instance method with the given name, return type
	 * and arguments.     
	 * 
	 * Params:
	 * 	   R = the return type
	 *     name = the name (selector) of the method to invoke
	 *     ARGS = the type of the arguments
	 *     args = the arguments to the method 
	 *     
	 * Returns: whatever the method returns
	 */
	private R invokeObjcSelf (R, dstep.internal.String.string name, ARGS...) (ARGS args)
	{
		return dstep.objc.bridge.Bridge.Bridge.invokeObjcMethod!(R, name, ARGS)(this.objcObject, args);
	}
	
	/**
	 * Invoke on the receiver's super part the instance method with the given name,
	 * return type and arguments.     
	 * 
	 * Params:
	 *     R = the return type
	 *     name = the name (selector) of the method to invoke
	 *     ARGS = the type of the arguments
	 *     args = the arguments to the method 
	 *     
	 * Returns: whatever the method returns
	 */
	private R invokeObjcSuper (R, dstep.internal.String.string name, ARGS...) (ARGS args)
	{
		return dstep.objc.bridge.Bridge.Bridge.invokeObjcSuperMethod!(R, name, ARGS)(this.objcSuper, args);
	}
	
	/**
	 * Invoke class method with given name, return type and arguments,
	 * on the receiver's superclass.     
	 * 
	 * Params:
	 * 	   R = the return type
	 *     name = the name (selector) of the method to invoke
	 *     ARGS = the type of the arguments
	 *     args = the arguments to the method 
	 *     
	 * Returns: whatever the method returns
	 */
	private static R invokeObjcSelfClass (R, dstep.internal.String.string name, ARGS...) (ARGS args)
	{
		return dstep.objc.bridge.Bridge.Bridge.invokeObjcClassMethod!(R, name, ARGS)(objcClass, args);
	}
	
	/**
	 * Invoke class method with given name, return type and arguments,
	 * on the receiver's superclass.     
	 * 
	 * Params:
	 * 	   R = the return type
	 *     name = the name (selector) of the method to invoke
	 *     ARGS = the type of the arguments
	 *     args = the arguments to the method 
	 *     
	 * Returns: whatever the method returns
	 */
	private static R invokeObjcSuperClass (R, dstep.internal.String.string name, ARGS...) (ARGS args)
	{		
		return dstep.objc.bridge.Bridge.Bridge.invokeObjcClassMethod!(R, name, ARGS)(objcSuperClass, args);
	}
}

/**
 * Creates a new Objective-C subclass, initializes its data and register it with the
 * Objective-C runtime.
 * 
 * Params:
 * 	   className = the name of the subclass 
 *     superClass = the subclass' superclass
 *     instanceMethods = the instance methods that should be added to the subclass
 *     classMethods = the class methods that should be added to the subclass
 *     
 * Returns: the newly created subclass
 */
Class subclassInit (string className) (Class superClass, Method[] instanceMethods = null, Method[] classMethods = null)
{
	Class cls;
	
	if (objc.getClass!(className))
		cls = objc.allocateClassPair!("D_" ~ className)(superClass, 0);
	
	else
		cls = objc.allocateClassPair!(className)(superClass, 0);
	
	Class metaClass = (cast(id) cls).getClass;
	
	ubyte alignment = cast(ubyte) log2(Bridge.DObjectType.sizeof);
	cls.addIvar!(Bridge.dObjectVar, encode!(Bridge.DObjectType))(Bridge.DObjectType.sizeof, alignment);
	
	foreach (method ; instanceMethods)
		bindings.class_addMethod(cls, method.method_name, method.method_imp, method.method_types);
	
	foreach (method ; classMethods)
		bindings.class_addMethod(metaClass, method.method_name, method.method_imp, method.method_types);
	
	objc.registerClassPair(cls);
	
	return cls;
}