Commits

w...@c90b9560-bf6c-de11-be94-00142212c4b1  committed 7c1650b

Add BridgeSupport support (been sitting around on my laptop for some time...)
Refactor method invocation code so we don't have 3 slightly different versions
of it.

  • Participants
  • Parent commits 08febb0

Comments (0)

Files changed (8)

 
 PHPCONFIG=php-config
-PHPCFLAGS=-DPHP_ATOM_INC -DCOMPILE_DL_OBJC -DPIC -I. `$(PHPCONFIG) --includes` -Ilibffi/include -g -Wall 
+PHPCFLAGS=-DPHP_ATOM_INC -DCOMPILE_DL_OBJC -DPIC -I. `$(PHPCONFIG) --includes` -Ilibffi/include -g -Wall  -I/usr/include/libxml2
 
 all: objc.so
 
-PHPOBJC_OBJS=extension.o exception.o
+PHPOBJC_OBJS=extension.o exception.o bridgesupport.o
 
 *.m: php_objc.h
 
 
 http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html#//apple_ref/doc/uid/TP40001418
 
+http://trac.macosforge.org/projects/BridgeSupport
 
  - _C_UNION
  - _C_STRUCT
 
+- Expand BridgeSupport code:
+  - refactor method bridging code so it can be used for functions too?
+  - allows us to create a generic struct class and map them as php classes
+

File bridgesupport.m

+/* vim:ts=4:sw=4:noet:
+ * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
+ * Copyright (c) 2008, Message Systems, Inc.
+ * Author: Wez Furlong, <wez@omniti.com>
+ * This source file is subject to version 3.01 of the PHP license.
+ */
+#include "php_objc.h"
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+
+/* This file implements BridgeSupport handling.
+ * http://trac.macosforge.org/projects/BridgeSupport
+ * Framework descriptions compiled into XML format can be
+ * used to augment the introspection of the Objective-C runtime pieces.
+ */
+static php_objc_function_ctx *parse_function(xmlNode *cur, const char *name, char *lname TSRMLS_DC);
+
+static PHP_FUNCTION(objc_bridge_func)
+{
+	char *fname = get_active_function_name(TSRMLS_C);
+	php_objc_function_ctx *fctx, **fctxp;
+	ffi_cif cif;
+	void **ffi_args = NULL;
+	ffi_type **ffi_arg_types = NULL;
+	zval ***args = NULL;
+	HashTable *byref_hash = NULL;
+	int i;
+	int nargs;
+	int is_ref;
+	const char *argtype;	
+	int first_passed = 0;
+	int nargs_passed = ZEND_NUM_ARGS();
+	php_objc_obj *obj;
+	void *rvalue = NULL;
+	int is_printf = 0;
+
+	if (FAILURE == zend_ts_hash_find(&php_objc_funcs, fname, strlen(fname),
+			(void**)&fctxp)) {
+		return;
+	}
+	fctx = *fctxp;
+
+	nargs = fctx->nargs;
+	if (nargs_passed < nargs) {
+		WRONG_PARAM_COUNT;
+	}
+	if (nargs_passed > nargs && !fctx->variadic) {
+		WRONG_PARAM_COUNT;
+	}
+
+	args = (zval ***)safe_emalloc(sizeof(zval *), nargs_passed, 0);
+	zend_get_parameters_array_ex(nargs_passed, args);
+
+	ffi_arg_types = calloc(nargs_passed, sizeof(ffi_type*));
+	ffi_args = calloc(nargs_passed, sizeof(void*));
+
+NS_DURING
+
+	ALLOC_HASHTABLE(byref_hash);
+	zend_hash_init(byref_hash, 2, NULL, NULL, 0);
+	
+	for (i = 0; i < nargs; i++) {
+		is_ref = 0;
+
+		if (fctx->args[i]->is_printf) {
+			is_printf = 1;
+		}
+		argtype = fctx->args[i]->type->typestr;
+		ffi_arg_types[i] = fctx->args[i]->type->ffi_type;
+		
+		while (*argtype) {
+			if (*argtype == 'N' || *argtype == 'o' || *argtype == 'R') {
+				is_ref = 1;
+				argtype++;
+				continue;
+			}
+			if (*argtype == 'r' || *argtype == 'n' || *argtype == 'O'
+					|| *argtype == 'V') {
+				argtype++;
+				continue;
+			}
+			/* must be the actual type code now */
+			break;
+		}
+
+		if (!is_ref && is_implied_ref_param(argtype)) {
+			argtype++;
+			is_ref = 1;
+		}
+
+		switch (*argtype) {
+			case _C_CHARPTR:
+				if (is_ref) {
+					php_objc_throw(
+						"references to charptr are not currently supported"
+						TSRMLS_CC);
+					return;
+				}
+
+				convert_to_string_ex(args[i + first_passed]);
+				ffi_args[i] = &Z_STRVAL_PP(args[i + first_passed]);
+				break;
+			case _C_SEL: {
+				SEL s;
+				
+				if (is_ref) {
+					php_objc_throw(
+						"references to SEL are not currently supported"
+						TSRMLS_CC);
+					return;
+				}
+
+				convert_to_string_ex(args[i + first_passed]);
+				s = php_objc_method_name_to_SEL(
+							Z_STRVAL_PP(args[i + first_passed]),
+							Z_STRLEN_PP(args[i + first_passed]));
+
+				zend_hash_index_update(byref_hash, i, &s, sizeof(s),
+					(void**)&ffi_args[i]);
+				break;
+			}
+			case _C_CLASS: {
+				Class c;
+				if (is_ref) {
+					php_objc_throw(
+						"references to CLASS are not currently supported"
+						TSRMLS_CC);
+					return;
+				}
+
+				convert_to_string_ex(args[i + first_passed]);
+				c = objc_getClass(Z_STRVAL_PP(args[i + first_passed]));
+				zend_hash_index_update(byref_hash, i, &c, sizeof(c),
+					(void**)&ffi_args[i]);
+
+				break;
+			}
+			case _C_STRUCT_B: {
+				int size;
+
+				if (is_ref) {
+					php_objc_throw(
+						"references to structures are not currently supported"
+						TSRMLS_CC);
+					return;
+				}
+
+				size = ffi_arg_types[i]->size;
+
+				/* FIXME: evil hack! */
+				convert_to_string_ex(args[i + first_passed]);
+				if (Z_STRLEN_PP(args[i + first_passed]) > size) {
+					php_objc_throw("struct parameter too big! %d expected, got %d"
+						TSRMLS_CC, size, Z_STRLEN_PP(args[i + first_passed]));
+					return;
+				}
+				if (Z_STRLEN_PP(args[i + first_passed]) != size) {
+					printf("struct parameter size mismatch: %d expected, got %d\n",
+						size, Z_STRLEN_PP(args[i + first_passed]));
+				}
+
+				ffi_args[i] = Z_STRVAL_PP(args[i + first_passed]);
+				break;
+			}
+			case _C_INT:
+			case _C_UINT:
+			{
+				int ival;
+				if (is_ref) {
+					php_objc_throw(
+						"references to int are not currently supported"
+						TSRMLS_CC);
+					return;
+				}
+
+				convert_to_long_ex(args[i + first_passed]);
+				ival = Z_LVAL_PP(args[i + first_passed]);
+				zend_hash_index_update(byref_hash, i, &ival, sizeof(ival),
+					(void**)&ffi_args[i]);
+
+				break;
+			}
+			case _C_LNG:
+			case _C_ULNG:
+				if (is_ref) {
+					php_objc_throw(
+						"references to long are not currently supported"
+						TSRMLS_CC);
+					return;
+				}
+
+				convert_to_long_ex(args[i + first_passed]);
+				ffi_args[i] = &Z_LVAL_PP(args[i + first_passed]);
+				break;
+			case _C_SHT:
+			case _C_USHT:
+			{
+				short sval;
+				if (is_ref) {
+					php_objc_throw(
+						"references to short are not currently supported"
+						TSRMLS_CC);
+					return;
+				}
+
+				convert_to_long_ex(args[i + first_passed]);
+				sval = Z_LVAL_PP(args[i + first_passed]);
+				zend_hash_index_update(byref_hash, i, &sval, sizeof(sval),
+					(void**)&ffi_args[i]);
+				break;
+			}
+			case _C_DBL:
+				if (is_ref) {
+					php_objc_throw(
+						"references to double are not currently supported"
+						TSRMLS_CC);
+					return;
+				}
+
+				convert_to_double_ex(args[i + first_passed]);
+				ffi_args[i] = &Z_DVAL_PP(args[i + first_passed]);
+				break;
+			case _C_FLT:
+			{
+				float fval;
+				if (is_ref) {
+					php_objc_throw(
+						"references to float are not currently supported"
+						TSRMLS_CC);
+					return;
+				}
+	
+				convert_to_double_ex(args[i + first_passed]);
+				fval = Z_DVAL_PP(args[i + first_passed]);
+				zend_hash_index_update(byref_hash, i, &fval, sizeof(fval),
+					(void**)&ffi_args[i]);
+				break;
+			}
+
+			case _C_CHR:
+			case _C_UCHR:
+			{
+				char cval;
+				if (is_ref) {
+					php_objc_throw(
+						"references to char are not currently supported"
+						TSRMLS_CC);
+					return;
+				}
+				cval = Z_LVAL_PP(args[i + first_passed]);
+				zend_hash_index_update(byref_hash, i, &cval, sizeof(cval),
+					(void**)&ffi_args[i]);
+				break;
+			}
+
+			case _C_ID: {
+				id idval = 0;
+
+				if (!php_objc_mapping(*args[i + first_passed],
+						&idval, 1 TSRMLS_CC)) {
+					return;
+				}
+
+				if (is_ref) {
+					id *idptr = NULL;
+					zend_hash_index_update(byref_hash, i, &idptr, sizeof(idptr),
+						(void**)&idptr);
+					*idptr = idval;
+					ffi_args[i] = idptr;
+				} else {
+					zend_hash_index_update(byref_hash, i, &idval, sizeof(idval),
+						(void**)&ffi_args[i]);
+				}
+
+				break;
+			}
+
+			case _C_PTR:
+				if (is_ref) {
+					php_objc_throw(
+						"references to ptr are not currently supported"
+						TSRMLS_CC);
+					return;
+				}
+
+				if (Z_TYPE_PP(args[i + first_passed]) == IS_NULL) {
+					id idval = 0;
+					zend_hash_index_update(byref_hash, i, &idval, sizeof(idval),
+						(void**)&ffi_args[i]);
+				} else {
+					php_objc_throw("can't handle non-null pointer type %s"
+						TSRMLS_CC, argtype);
+					return;
+				}
+				break;
+
+			default:
+				php_objc_throw("unhandled input arg type %s (is_ref=%d)"
+					TSRMLS_CC, argtype, is_ref);
+				return;
+		}
+	}
+
+	/* support variable args by presenting them as objects */
+	for (i = nargs; i < nargs_passed; i++) {
+		ffi_arg_types[i] = &ffi_type_pointer;
+
+		if (Z_TYPE_PP(args[i + first_passed]) == IS_OBJECT) {
+			php_objc_class *cls;
+			php_objc_obj *obj;
+			zend_class_entry *ce = Z_OBJCE_PP(args[i + first_passed]);
+
+			if (zend_ts_hash_find(&php_objc_imported_classes, ce->name,
+						strlen(ce->name), (void**)&cls) == FAILURE) {
+				php_objc_throw("can't pass %s to Objective-C" TSRMLS_CC,
+						ce->name);
+				return;
+			}
+
+			obj = PHPOBJC(*args[i + first_passed]);
+			ffi_args[i] = &obj->instance;
+		} else if (Z_TYPE_PP(args[i + first_passed]) == IS_NULL) {
+			id idval = 0;
+			zend_hash_index_update(byref_hash, i, &idval, sizeof(idval),
+				(void**)&ffi_args[i]);
+		} else if (is_printf && Z_TYPE_PP(args[i + first_passed]) == IS_STRING) {
+			/* PHP strings go in as-is for printf style functions */
+			char *str = Z_STRVAL_PP(args[i + first_passed]);
+			zend_hash_index_update(byref_hash, i, &str, sizeof(str),
+				(void**)&ffi_args[i]);
+
+		} else if (Z_TYPE_PP(args[i + first_passed]) == IS_STRING) {
+			/* map PHP strings to NSString */
+			NSString *str = [[NSString alloc]
+					initWithUTF8String:Z_STRVAL_PP(args[i + first_passed])];
+			zend_hash_index_update(byref_hash, i, &str, sizeof(str),
+				(void**)&ffi_args[i]);
+		} else {
+			php_objc_throw("expected object for %s" TSRMLS_CC, argtype);
+			return;
+		}
+	}
+
+	ffi_prep_cif(&cif, FFI_DEFAULT_ABI, nargs_passed,
+		fctx->retval ? fctx->retval->type->ffi_type : &ffi_type_void,
+		ffi_arg_types);
+
+	switch (fctx->retval ? fctx->retval->type->ffi_type->type : FFI_TYPE_VOID) {
+#ifdef __i386__
+		case FFI_TYPE_FLOAT:
+		{
+			float fval;
+			if (obj && obj->from_objc) {
+				php_objc_throw("floating point return types are not currently supported for parent:: calls" TSRMLS_CC);
+			}
+			ffi_call(&cif, FFI_FN(objc_msgSend_fpret), &fval, ffi_args);
+			RETVAL_DOUBLE(fval);
+		}
+
+		case FFI_TYPE_DOUBLE:
+		{
+			if (obj && obj->from_objc) {
+				php_objc_throw("floating point return types are not currently supported for parent:: calls" TSRMLS_CC);
+			}
+			RETVAL_DOUBLE(0);
+			ffi_call(&cif, FFI_FN(objc_msgSend_fpret), &Z_DVAL_P(return_value), ffi_args);
+			break;
+		}
+#endif
+		case FFI_TYPE_STRUCT:
+			php_objc_throw("structure return types are not currently supported" TSRMLS_CC);
+			break;
+		default: {
+			union {
+				float fval;
+				int ival;
+				unsigned int uival;
+				char cval;
+				unsigned char ucval;
+				short sval;
+				unsigned short usval;
+				id idval;
+				SEL selval;
+				Class classval;
+			} u;
+
+			argtype = fctx->retval ? fctx->retval->type->typestr : "v";
+			while (*argtype) {
+				if (*argtype == 'N' || *argtype == 'o' || *argtype == 'R' ||
+						*argtype == 'r' || *argtype == 'n' || *argtype == 'O'
+						|| *argtype == 'V') {
+					argtype++;
+					continue;
+				}
+				/* must be the actual type code now */
+				break;
+			}
+			switch (*argtype) {
+				case _C_DBL:
+					RETVAL_DOUBLE(0);
+					rvalue = &Z_DVAL_P(return_value);
+					break;
+				case _C_LNG:
+				case _C_ULNG:
+					RETVAL_LONG(0);
+					rvalue = &Z_LVAL_P(return_value);
+					break;
+				case _C_FLT: rvalue = &u.fval; break;
+				case _C_SHT: rvalue = &u.sval; break;
+				case _C_USHT: rvalue = &u.usval; break;
+				case _C_INT: rvalue = &u.ival; break;
+				case _C_UINT: rvalue = &u.uival; break;
+				case _C_CHR: rvalue = &u.cval; break;
+				case _C_UCHR: rvalue = &u.ucval; break;
+				case _C_ID: rvalue = &u.idval; break;
+				case _C_VOID: rvalue = NULL; break;
+				default:
+					printf("Can't return %s\n", argtype);
+					abort();
+			}
+			
+			if (!fctx->sym) {
+				fctx->sym = dlsym(RTLD_DEFAULT, fctx->name);
+			}
+			if (!fctx->sym) {
+				printf("Couldn't resolve symbol %s\n", fctx->name);
+				RETVAL_NULL();
+			} else {
+				ffi_call(&cif, FFI_FN(fctx->sym), rvalue, ffi_args);
+
+				switch (*argtype) {
+					case _C_FLT: RETVAL_DOUBLE(u.fval); break;
+					case _C_INT: RETVAL_LONG(u.ival); break;
+					case _C_UINT:RETVAL_LONG(u.uival); break;
+					case _C_SHT: RETVAL_LONG(u.sval); break;
+					case _C_USHT:RETVAL_LONG(u.usval); break;
+					case _C_CHR: RETVAL_LONG(u.cval); break;
+					case _C_UCHR: RETVAL_LONG(u.ucval); break;
+					case _C_ID:
+						if (u.idval) {
+							php_objc_wrap_id(u.idval, return_value);
+						} else {
+							RETVAL_NULL();
+						}
+						break;
+
+					default:
+						;
+				}
+			}
+		}
+	}
+
+	if (byref_hash) {
+		/* now fixup by-ref parameters */
+		for (i = 0; i < nargs; i++) {
+			is_ref = 0;
+
+			argtype = fctx->args[i]->type->typestr;
+
+			while (*argtype) {
+				if (*argtype == 'N' || *argtype == 'o' || *argtype == 'R') {
+					is_ref = 1;
+					argtype++;
+					continue;
+				}
+				if (*argtype == 'r' || *argtype == 'n' || *argtype == 'O'
+						|| *argtype == 'V') {
+					argtype++;
+					continue;
+				}
+				/* must be the actual type code now */
+				break;
+			}
+			if (!is_ref && is_implied_ref_param(argtype)) {
+				argtype++;
+				is_ref = 1;
+			}
+	
+			if (!is_ref) continue;
+
+			SEPARATE_ZVAL_IF_NOT_REF(args[i + first_passed]);
+			switch (*argtype) {
+				case _C_ID: {
+					id *idptr;
+					
+					zend_hash_index_find(byref_hash, i, (void**)&idptr);
+
+					if (*idptr) {
+						php_objc_wrap_id(*idptr, *args[i + first_passed]);
+					} else {
+						ZVAL_NULL(*args[i + first_passed]);
+					}
+					break;
+				}
+				default:
+		  			php_objc_throw(
+						"unhandled byref input arg type %s (is_ref=%d)"
+						TSRMLS_CC, argtype, is_ref);
+					return;
+			}
+		}
+		zend_hash_destroy(byref_hash);
+		FREE_HASHTABLE(byref_hash);
+	}
+
+NS_HANDLER
+	php_objc_throw_NS(localException TSRMLS_CC);
+NS_ENDHANDLER
+}
+
+/* compile an objective-C type string down to a structured type */
+php_objc_struct_type *php_objc_compile_struct(const char *name,
+	const char *typestr, char **next_pos)
+{
+	php_objc_struct_type s, *sptr;
+	char *str;
+	smart_str undecorated = {0};
+	HashPosition pos;
+	int i;
+
+	memset(&s, 0, sizeof(s));
+
+	if (typestr[0] != '{') {
+		return NULL;
+	}
+
+	/* typestr might, or might not, contain field names; we need to pull
+	 * those out and record a version of the type descriptor without them,
+	 * so that we can map undecorated versions of the same type back to
+	 * this nice type description */
+	str = strchr(typestr, '=');
+	if (!str) {
+		str = strchr(typestr, '}');
+	}
+
+	if (name) {
+		s.name = estrdup(name);
+	} else {
+		/* infer the name from the typestr */
+		s.name = estrndup(typestr+1, str - (typestr+1));
+	}
+//	printf("Compiling struct %s %s\n", s.name, str);
+	if (*str == '=') {
+		str++;
+		zend_hash_init(&s.members, 2, NULL, NULL, 0);
+	}
+	smart_str_appendl(&undecorated, typestr, str - typestr);
+
+// NSSize {_NSSize="width"f"height"f}
+
+	while (*str != '}') {
+		php_objc_struct_member m;
+		char *ptr, *ptrstart;
+
+//printf("undec: %.*s\n", undecorated.len, undecorated.c);
+		memset(&m, 0, sizeof(m));
+
+		if (*str == '"') {
+			/* field name */
+			ptr = strchr(str + 1, '"');
+			m.name = estrndup(str + 1, ptr - (str+1));
+			str = ptr + 1;
+		} else {
+			char namebuf[32];
+			snprintf(namebuf, sizeof(namebuf)-1, "f%d",
+				zend_hash_num_elements(&s.members));
+			m.name = estrdup(namebuf);
+		}
+
+		/* now we're on an objc-type identifier */
+		ptrstart = str;
+//printf("name=%s and type is %c\n", m.name, str[0]);
+		if (str[0] == _C_PTR) {
+			str++;
+			switch (*str) {
+				case _C_VOID:
+				case _C_UNDEF: /* '?' */
+					m.ffi_type = &ffi_type_pointer;
+					break;
+				case _C_STRUCT_B:
+					m.struct_type = php_objc_compile_struct(NULL, str, &ptr);
+					if (m.struct_type == NULL) {
+						goto cant_map;
+					}
+					/* FIXME: this is a pointer to a struct */
+					goto cant_map;
+					break;
+				default:
+					goto cant_map;
+			}
+		} else switch (*str) {
+			case _C_SEL:
+			case _C_CLASS:
+			case _C_CHARPTR:
+			case _C_ID:
+			case _C_UNDEF:
+				m.ffi_type = &ffi_type_pointer;
+				break;
+			case _C_CHR:
+				m.ffi_type = &ffi_type_schar;
+				break;
+			case _C_UCHR:
+			case 'B':
+				m.ffi_type = &ffi_type_uchar;
+				break;
+			case _C_SHT:
+				m.ffi_type = &ffi_type_sshort;
+				break;
+			case _C_USHT:
+				m.ffi_type = &ffi_type_ushort;
+				break;
+			case _C_INT:
+				m.ffi_type = &ffi_type_sint;
+				break;
+			case _C_UINT:
+				m.ffi_type = &ffi_type_uint;
+				break;
+			case _C_LNG:
+				m.ffi_type = &ffi_type_slong;
+				break;
+			case _C_ULNG:
+				m.ffi_type = &ffi_type_ulong;
+				break;
+			case _C_FLT:
+				m.ffi_type = &ffi_type_float;
+				break;
+			case _C_DBL:
+				m.ffi_type = &ffi_type_double;
+				break;
+			case 'Q':
+				m.ffi_type = &ffi_type_uint64;
+				break;
+			case 'q':
+				m.ffi_type = &ffi_type_sint64;
+				break;
+			case _C_VOID:
+				m.ffi_type = &ffi_type_void;
+				break;
+			case _C_STRUCT_B:
+				m.struct_type = php_objc_compile_struct(NULL, str, &ptr);
+				if (m.struct_type == NULL) {
+					goto cant_map;
+				}
+				break;
+			case _C_ARY_B:
+			case _C_BFLD:
+				goto cant_map;
+			default:
+				printf("Whoops, not sure what to do with %s\n", str);
+				goto cant_map;
+				abort();
+		}
+		if (m.struct_type) {
+			smart_str_appendl(&undecorated, m.struct_type->objctype,
+				strlen(m.struct_type->objctype));
+		} else {
+			ptr = str+1;
+			smart_str_appendl(&undecorated, ptrstart, ptr - ptrstart);
+		}
+
+		zend_hash_update(&s.members, m.name, strlen(m.name),
+			&m, sizeof(m), NULL);
+
+		str = ptr;
+	}
+
+	smart_str_appendl(&undecorated, "}", 1);
+	smart_str_0(&undecorated);
+	s.objctype = undecorated.c;
+//	printf("done: %s\n", s.objctype);
+
+	s.ffi_type.type = FFI_TYPE_STRUCT;
+	s.ffi_type.elements = ecalloc(1 + zend_hash_num_elements(&s.members),
+							sizeof(s.ffi_type.elements[0]));
+	zend_hash_internal_pointer_reset_ex(&s.members, &pos);
+	i = 0;
+	do {
+		php_objc_struct_member *m;	
+		if (FAILURE == zend_hash_get_current_data_ex(&s.members, &m, &pos)) {
+			break;
+		}
+		s.ffi_type.elements[i] = m->ffi_type;
+		i++;
+	} while (zend_hash_move_forward_ex(&s.members, &pos) == SUCCESS);
+	s.ffi_type.elements[i] = NULL;
+
+	/* FIXME: need to declare a dtor on php_objc_structs, as we can and
+	 * do parse the same structure definitions multiple times.
+	 * We should also consider detecting that case and being smart.
+	 * Pathological example is __GLIFunctionDispatchRec */
+	zend_ts_hash_update(&php_objc_structs, s.objctype, strlen(s.objctype),
+		&s, sizeof(s), (void**)&sptr);
+
+	if (next_pos) {
+		*next_pos = str+1;
+	}
+
+	return sptr;
+
+cant_map:
+	return NULL;
+}
+
+static inline char *attr_string(xmlNode *node, const char *name,
+	const char *name64)
+{
+	char *value = (char*)xmlGetProp(node, (xmlChar*)name);
+#ifdef __LP64__
+	if (name64) {
+		char *v64 = xmlGetProp(node, name64);
+		if (v64) {
+			if (value) {
+				xmlFree(value);
+			}
+			value = v64;
+		}
+	}
+#endif
+	return value;
+}
+
+static int attr_int(xmlNode *node, const char *name, int *value_ptr)
+{
+	char *value = (char*)xmlGetProp(node, (xmlChar*)name);
+	if (value) {
+		*value_ptr = atoi(value);
+		xmlFree(value);
+		return 1;
+	}
+	return 0;
+}
+
+static int attr_bool(xmlNode *node, const char *name, int defval)
+{
+	char *value = (char*)xmlGetProp(node, (xmlChar*)name);
+	int ret = defval;
+	if (value) {
+		if (!strcmp(value, "true")) {
+			ret = 1;
+		} else if (!strcmp(value, "false")) {
+			ret = 0;
+		}
+		xmlFree(value);
+	}
+	return ret;
+}
+
+static php_objc_arg *parse_arg(xmlNode *cur, int isret)
+{
+	php_objc_arg *arg = ecalloc(1, sizeof(*arg));
+	char *typestr;
+
+#if 0
+	typestr = attr_string(cur, "declared_type", NULL);
+	if (typestr) {
+		/* look up this type in the structs hash */
+printf("declared as %s\n", typestr);
+		xmlFree(typestr);
+	}
+#endif
+	if (!arg->type) {
+		typestr = attr_string(cur, "type", "type64");
+		if (zend_ts_hash_find(&php_objc_types, typestr, strlen(typestr),
+					(void**)&arg->type) == FAILURE) {
+			php_objc_type_info tinfo;
+
+
+			tinfo.typestr = estrdup(typestr);
+
+			php_objc_type_to_ffi(tinfo.typestr, &tinfo.ffi_type, isret);
+			zend_ts_hash_update(&php_objc_types, typestr, strlen(typestr),
+					&tinfo, sizeof(tinfo), (void**)&arg->type);
+		}
+		xmlFree(typestr);
+	}
+
+	arg->name = attr_string(cur, "name", NULL);
+
+	if (attr_int(cur, "c_array_length_in_arg", &arg->array_length)) {
+		arg->array_type = php_objc_arr_size_is;
+	} else if (attr_int(cur, "c_array_of_fixed_length", &arg->array_length)) {
+		arg->array_type = php_objc_arr_fixed_size;
+	} else if (attr_bool(cur, "c_array_delimited_by_null", 0)) {
+		arg->array_type = php_objc_arr_null_terminated;
+	} else if (attr_bool(cur, "c_array_of_variable_length", 0)) {
+		arg->array_type = php_objc_arr_variable;
+	} else if (attr_bool(cur, "c_array_length_in_retval", 0)) {
+		arg->array_type = php_objc_arr_return_length;
+	}
+
+	typestr = attr_string(cur, "type_modifier", NULL);
+	if (typestr) {
+		switch (typestr[0]) {
+			case 'n':
+				arg->direction = php_objc_arg_in;
+				break;
+			case 'o':
+				arg->direction = php_objc_arg_out;
+				break;
+			case 'N':
+				arg->direction = php_objc_arg_inout;
+				break;
+		}
+		xmlFree(typestr);
+	}
+
+	arg->null_ok = attr_bool(cur, "null_accepted", 0);
+	arg->is_printf = attr_bool(cur, "printf_format", 0);
+	arg->already_retained = attr_bool(cur, "already_retained", 0);
+
+	if (attr_bool(cur, "function_pointer", 0)) {
+		arg->function_ptr = parse_function(cur, NULL, NULL TSRMLS_CC);
+	}
+
+	arg->sel_of_type = attr_string(cur, "sel_of_type", "sel_of_type64");
+
+	return arg;
+}
+
+static php_objc_function_ctx *parse_function(xmlNode *cur, const char *name,
+		char *lname TSRMLS_DC)
+{
+	php_objc_function_ctx *fctx = ecalloc(1, sizeof(*fctx));
+
+	if (name) {
+		fctx->name = estrdup(name);
+	}
+	fctx->variadic = attr_bool(cur, "variadic", 0);
+
+	for (cur = cur->children; cur; cur = cur->next) {
+		if (cur->type != XML_ELEMENT_NODE) {
+			continue;
+		}
+		const char *tag = (const char*)cur->name;
+		if (!strcmp(tag, "retval")) {
+			fctx->retval = parse_arg(cur, 1);
+		} else if (!strcmp(tag, "arg")) {
+			php_objc_arg *arg = parse_arg(cur, 0);
+
+			fctx->nargs++;
+			fctx->args = erealloc(fctx->args, fctx->nargs * sizeof(arg));
+			fctx->args[fctx->nargs - 1] = arg;
+
+		} else {
+			continue;
+		}
+	}
+
+	if (lname) {
+		zend_internal_function zif;
+		memset(&zif, 0, sizeof(zif));
+
+		zif.type = ZEND_INTERNAL_FUNCTION;
+		zif.handler = PHP_FN(objc_bridge_func);
+		zif.function_name = fctx->name;
+
+		/* TODO: build and populate zif.arg_info */
+
+		zend_hash_add(EG(function_table), lname, strlen(lname)+1,
+			&zif, sizeof(zif), NULL);
+		zend_ts_hash_update(&php_objc_funcs, fctx->name, strlen(fctx->name),
+			&fctx, sizeof(fctx), NULL);
+	}
+
+	return fctx;
+}
+
+
+void php_objc_load_framework(const char *frameworkname TSRMLS_DC)
+{
+	NSBundle *bundle;
+	NSString *str, *bsname;
+	char framework[1024];
+	char filename[1024];
+	char bridgesup[1024];
+	char *strptr;
+	xmlDoc *doc;
+
+	strptr = strrchr(frameworkname, '/');
+	if (strptr) {
+		snprintf(filename, sizeof(filename)-1, "%s", frameworkname);
+		snprintf(framework, sizeof(framework)-1, "%s", strptr + 1);
+		strptr = strrchr(framework, '.');
+		if (strptr) {
+			*strptr = '\0';
+		}
+	} else {
+		snprintf(filename, sizeof(filename)-1,
+			"/System/Library/Frameworks/%s.framework", frameworkname);
+		snprintf(framework, sizeof(framework)-1, "%s", frameworkname);
+	}
+	
+	// FIXME only do this if we need to; should hash filename
+	
+	/* load it up */
+	str = [[NSString alloc] initWithUTF8String:filename];
+	bundle = [[NSBundle alloc] initWithPath:str];
+	if (!bundle) {
+		return;
+	}
+	[bundle load];
+	str = nil;
+
+	/* do we have a .bridgesupport file ? */
+	bsname = [bundle resourcePath];
+	bsname = [bsname stringByAppendingFormat:@"/%@", @"BridgeSupport"];
+	if (bsname) {
+		snprintf(bridgesup, sizeof(bridgesup)-1, "%s/%sFull.bridgesupport",
+			[bsname fileSystemRepresentation], framework);
+		if (access(bridgesup, R_OK) != 0) {
+			snprintf(bridgesup, sizeof(bridgesup)-1, "%s/%s.bridgesupport",
+				[bsname fileSystemRepresentation], framework);
+		}
+		bsname = nil;
+	} else {
+		bridgesup[0] = '\0';
+	}
+//printf("Thinking about %s\n", bridgesup);
+	if (bridgesup[0] == '\0') {
+		snprintf(bridgesup, sizeof(bridgesup)-1,
+			"/Library/BridgeSupport/%s.bridgesupport", framework);
+		if (access(bridgesup, R_OK) != 0) {
+			char *home = getenv("HOME");
+			if (home) {
+				snprintf(bridgesup, sizeof(bridgesup)-1,
+					"%s/Library/BridgeSupport/%s.bridgesupport",
+					home, framework);
+				if (access(bridgesup, R_OK) != 0) {
+					/* FIXME: make this path an ini parameter */
+					snprintf(bridgesup, sizeof(bridgesup)-1,
+						"/tmp/BridgeSupportTiger/Release/Library/BridgeSupport/%s.bridgesupport",
+						framework);
+					if (access(bridgesup, R_OK) != 0) {
+						bridgesup[0] = '\0';
+					}
+				}
+			}
+		}
+	}
+	if (bridgesup[0] == '\0') {
+		return;
+	}
+
+	if (access(bridgesup, R_OK) != 0) {
+		return;
+	}
+	if (zend_ts_hash_find(&php_objc_imported_frameworks,
+			bridgesup, strlen(bridgesup), (void**)&strptr) == SUCCESS) {
+		return;
+	}
+	zend_ts_hash_update(&php_objc_imported_frameworks,
+		bridgesup, strlen(bridgesup),
+		bridgesup, strlen(bridgesup), NULL);
+
+	doc = xmlReadFile(bridgesup, NULL, 0);
+	xmlNode *root = xmlDocGetRootElement(doc);
+	if (!root) {
+		return;
+	}
+
+	// if there is a dylib alongside the .bridgesupport file, we should
+	// dlopen it here; it contains inline functions from the framework headers
+	strptr = strrchr(bridgesup, '.');
+	// might need to backpedal if we picked up the Full version
+	// (blahFull.bridgesupport)
+	if (strptr > bridgesup + 5 && !strncasecmp(strptr - 4, "Full", 4)) {
+		strptr -= 4;
+		*strptr = '.';
+	}
+	strptr++;
+	strcpy(strptr, "dylib");
+	if (access(bridgesup, R_OK) == 0) {
+		dlopen(bridgesup, RTLD_LAZY);
+	}
+
+	if (root->type != XML_ELEMENT_NODE || 
+			strcmp((char*)root->name, "signatures")) {
+		printf("%s: invalid root node\n", bridgesup);
+		return; /* leak */
+	}
+	xmlNode *cur;
+	for (cur = root->children; cur != NULL; cur = cur->next) {
+		if (cur->type != XML_ELEMENT_NODE) {
+			continue;
+		}
+		const char *tag = (const char*)cur->name;
+		char *value;
+		if (!strcmp(tag, "depends_on")) {
+			value = attr_string(cur, "path", NULL);
+			if (value) {
+//				printf("loading %s\n", value);
+				php_objc_load_framework(value TSRMLS_CC);
+				xmlFree(value);
+			}
+		} else if (!strcmp(tag, "constant")) {
+			char *name = attr_string(cur, "name", NULL);
+			char *type = attr_string(cur, "type", "type64");
+			
+			// FIXME: could potentially support any type we can map
+			if (name && type && type[0] == '@') {
+				id **obj = (id**)dlsym(RTLD_DEFAULT, name);
+
+				// FIXME: should be a proxy object that binds on demand, as
+				// some of these guys aren't bound yet (like NSApp)
+				if (obj && *obj) {
+					zval *val;
+					zend_constant zc;
+
+					ALLOC_INIT_ZVAL(val);
+					php_objc_wrap_id((id)*obj, val TSRMLS_CC);
+					zc.value = *val;
+					zc.flags = CONST_CS;
+					zc.name = zend_strndup(name, strlen(name));
+					zc.name_len = strlen(name)+1;
+					zc.module_number = 0;
+					zend_register_constant(&zc TSRMLS_CC);
+				}
+
+			}
+			if (name) xmlFree(name);
+			if (type) xmlFree(type);
+		} else if (!strcmp(tag, "string_constant")) {
+			char *name = attr_string(cur, "name", NULL);
+			char *value = attr_string(cur, "value", NULL);
+			char *nsstring = attr_string(cur, "nsstring", NULL);
+		
+			if (nsstring && !strcmp(nsstring, "true")) {
+				NSString *str = [[NSString alloc] initWithUTF8String:value];
+				zval *val;
+				zend_constant zc;
+
+				ALLOC_INIT_ZVAL(val);
+				php_objc_wrap_id(str, val TSRMLS_CC);
+				zc.value = *val;
+				zc.flags = CONST_CS;
+				zc.name = zend_strndup(name, strlen(name));
+				zc.name_len = strlen(name)+1;
+				zc.module_number = 0;
+				zend_register_constant(&zc TSRMLS_CC);
+			} else {
+				zend_register_string_constant(name, strlen(name)+1,
+					estrdup(value), CONST_CS, 0 TSRMLS_CC);
+			}
+			if (name) xmlFree(name);
+			if (value) xmlFree(value);
+			if (nsstring) xmlFree(nsstring);
+
+		} else if (!strcmp(tag, "enum")) {
+			char *name = attr_string(cur, "name", NULL);
+			char *value = attr_string(cur, "value", NULL);
+			
+			if (name && value) {
+				long val = atol(value);
+				zend_register_long_constant(name, strlen(name)+1, val,
+					CONST_CS, 0 TSRMLS_CC);
+			}
+			if (name) xmlFree(name);
+			if (value) xmlFree(value);
+		} else if (!strcmp(tag, "function")) {
+			char *name = attr_string(cur, "name", NULL);
+			if (name) {
+				void *pfe;
+				char *lname = zend_str_tolower_dup(name, strlen(name));
+				if (zend_hash_find(EG(function_table), lname, strlen(lname)+1,
+						&pfe) == FAILURE) {
+					parse_function(cur, name, lname TSRMLS_CC);
+				}
+				efree(lname);
+				xmlFree(name);
+			}
+		} else if (!strcmp(tag, "struct")) {
+			char *name = attr_string(cur, "name", NULL);
+			char *typestr = attr_string(cur, "type", "type64");
+			if (name && typestr) {
+				php_objc_compile_struct(name, typestr, NULL);
+			}
+			if (name) xmlFree(name);
+			if (typestr) xmlFree(typestr);
+		} else {
+//	FIXME		printf("tag: :%s:\n", tag);
+		}
+	}
+	xmlFreeDoc(doc);
+}
 /* vim:ts=4:sw=4:noet:
  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
+ * Copyright (c) 2008, Message Systems, Inc.
  * Author: Wez Furlong, <wez@omniti.com>
  * This source file is subject to version 3.01 of the PHP license.
  */
 /* vim:ts=4:sw=4:noet:
  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
+ * Copyright (c) 2008, Message Systems, Inc.
  * Author: Wez Furlong, <wez@omniti.com>
  * This source file is subject to version 3.01 of the PHP license.
  */
 static zend_object_value php_objc_object_new(zend_class_entry *ce TSRMLS_DC);
 static int php_objc_method_call(char *method, INTERNAL_FUNCTION_PARAMETERS);
 static int php_objc_method_call_inner(char *method, int skip_arg_static, INTERNAL_FUNCTION_PARAMETERS);
-static void php_objc_wrap_id(id idval, zval *return_value);
-static int php_objc_mapping(zval *object, id *idval, int throw TSRMLS_DC);
-static const char *php_objc_type_to_ffi(const char *t, ffi_type **ffi, int isret);
 static struct objc_class *php_objc_export_class(zend_class_entry *ce TSRMLS_DC);
 static int php_objc_classHandler(const char *classname);
 static void php_objc_import_classes(TSRMLS_D);
+static char *php_objc_SEL_to_string(SEL s TSRMLS_DC);
 
 TsHashTable php_objc_imported_classes;
+TsHashTable php_objc_imported_frameworks;
 TsHashTable php_objc_structs;
+TsHashTable php_objc_types;
+TsHashTable php_objc_funcs;
 static NSAutoreleasePool *my_pool = NULL;
 
 zend_class_entry
 	efree(msg);
 }
 
-@implementation PHPZval /* {{{ */
-- (void)dealloc
+static void handle_method_invocation(NSInvocation *inv,
+	zval *zvthis
+	TSRMLS_DC)
 {
-	if (val) {
-		zval_ptr_dtor(&val);
-	}
-	[super dealloc];
-}
-
-- (zval *)_phpGetZval
-{
-	return val;
-}
-
-- (BOOL)respondsToSelector:(SEL)aSelector
-{
-	struct objc_method_list *mlist;
-	void *iter = 0;
-
-	while ((mlist = class_nextMethodList(self->isa, &iter))) {
-		int i;
-		for (i = 0; i < mlist->method_count; i++) {
-			if (mlist->method_list[i].method_name == aSelector) {
-				return YES;
-			}
+	NSMethodSignature *sig = [inv methodSignature];
+	int nargs = 0;
+	SEL sel = [inv selector];
+	char *buf, *cur;
+	zend_function *f = NULL;
+	zend_class_entry *ce = NULL;
+	zval ***params = NULL;
+	zval *func_name = NULL;
+	zval *retval = NULL;
+	int i;
+
+	nargs = [sig numberOfArguments] - 2;
+	buf = estrdup(sel_getName(sel));
+	for (cur = buf; *cur; cur++) {
+		if (*cur == ':') {
+			*cur = '_';
+		} else {
+			*cur = tolower(*cur);
 		}
 	}
-
-	{
-		char *buf;
-		char *cur;
-		BOOL retval = NO;
-		zend_function *f;
-		zend_class_entry *ce;
-
-		buf = strdup(sel_getName(aSelector));
-		for (cur = buf; *cur; cur++) {
-			if (*cur == ':')
-				*cur = '_';
-			else
-				*cur = tolower(*cur);
-		}
-
-		ce = Z_OBJCE_P(self->val);
-		if (zend_hash_find(&ce->function_table,
-				buf, strlen(buf)+1, (void**)&f) == SUCCESS) {
-			retval = YES;
-		} else {
-			printf("I don't respond to %s\n", buf);
-		}
-
-		if (retval == NO) {
-			/* TODO: call a respondsToSelector method if present */
-		}
-
-		free(buf);
-		return retval;
-	}
-
-	return NO;
-}
-
-- (void)forwardInvocation:(NSInvocation *)anInvocation
-{
-	SEL sel = [anInvocation selector];
-
-	if (sel == @selector(respondsToSelector:)) {
-		BOOL b;
-		[anInvocation getArgument:&sel atIndex:2];
-		b = [self respondsToSelector:sel];
-		[anInvocation setReturnValue:&b];
-		return;
-	}
-
-#if 0
-	if ([self respondsToSelector:sel]) {
-		[anInvocation setTarget:self];
-		[anInvocation invoke];
-		return;
-	}
-#endif
-	printf("I get to forward %s!\n", sel_getName(sel));
-	{
-		char *buf;
-		char *cur;
-		zend_function *f;
-		zend_class_entry *ce;
-		int nargs = 0;
-		zval ***params;
-		zval *func_name;
-		int i;
-		TSRMLS_FETCH();
-
-		buf = estrdup(sel_getName(sel));
-		for (cur = buf; *cur; cur++) {
-			if (*cur == ':') {
-				*cur = '_';
-				nargs++;
-			} else {
-				*cur = tolower(*cur);
-			}
-		}
-		ALLOC_INIT_ZVAL(func_name);
-		ZVAL_STRING(func_name, buf, 0);
-
-		ce = Z_OBJCE_P(self->val);
-		if (zend_hash_find(&ce->function_table,
-				buf, strlen(buf)+1, (void**)&f) == SUCCESS) {
-			zval *retval = NULL;
-
-			/* now my favourite part, calling into PHP */
-			if (nargs) {
-				params = (zval***)safe_emalloc(sizeof(zval **), nargs, 0);
-				for (i = 0; i < nargs; i++) {
-					zval *arg;
-					id iarg;
-
-					[anInvocation getArgument:&iarg atIndex:(i+2)];
-
-					params[i] = (zval**)emalloc(sizeof(zval**));
-					if ([iarg isKindOfClass:[PHPZval class]]) {
-						/* we get to ref the zval direct */
-						*params[i] = [iarg _phpGetZval];
-						ZVAL_ADDREF(*params[i]);
-					} else {
-						/* wrap it in */
-						ALLOC_INIT_ZVAL(arg);
-						php_objc_wrap_id(iarg, arg TSRMLS_CC);
-						*params[i] = arg;
-					}
-					
-				}
-			} else {
-				params = NULL;
-			}
-			zend_try {
-				if (SUCCESS == call_user_function_ex(&ce->function_table,
-						&self->val, func_name, &retval, nargs, params, 1, NULL
-						TSRMLS_CC)) {
-				}
-			} zend_catch {
-			} zend_end_try();
-			if (params) {
-				for (i = 0; i < nargs; i++) {
-					zval_ptr_dtor(params[i]);
-					efree(params[i]);
-				}
-				efree(params);
-			}
-			if (EG(exception)) {
-				if (retval) {
-					zval_ptr_dtor(&retval);
-				}
-				zval_ptr_dtor(&func_name);
-				php_objc_raise(EG(exception) TSRMLS_CC);
-			}
-
-			if (retval) {
-				switch (Z_TYPE_P(retval)) {
-					case IS_BOOL:
-					case IS_LONG:
-					{
-						long l = Z_LVAL_P(retval);
-						[anInvocation setReturnValue:&l];
-						break;
-					}
-					case IS_NULL:
-					{
-						id null = 0;
-						[anInvocation setReturnValue:&null];
-						break;
-					}
-					default:
-					{
-						id iret;
-						if (php_objc_mapping(retval, &iret, 0 TSRMLS_CC)) {
-							[anInvocation setReturnValue:&iret];
-						}
-					}
-				}
-				zval_ptr_dtor(&retval);
-			}
-		}
+	ALLOC_INIT_ZVAL(func_name);
+	ZVAL_STRING(func_name, buf, 0);
+
+	ce = Z_OBJCE_P(zvthis);
+	if (zend_hash_find(&ce->function_table,
+				buf, strlen(buf)+1, (void**)&f) != SUCCESS) {
 		zval_ptr_dtor(&func_name);
 		return;
 	}
-	
-	[super forwardInvocation:anInvocation];
-}
-
-- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
-{
-	char *sig;
-	struct objc_method *m;
-
-	m = class_getInstanceMethod(self->isa, aSelector);
-	if (m) {
-		sig = m->method_types;
-	} else {
-		const char *buf;
-		const char *cur;
-		int nargs = 0;
-
-		buf = sel_getName(aSelector);
-		for (cur = buf; *cur; cur++) {
-			if (*cur == ':') {
-				nargs++;
-			}
-		}
-
-		sig = alloca(nargs + 4);
-		memset(sig, '@', nargs + 3);
-		sig[nargs+3] = '\0';
-		sig[2] = ':';
-		sig[0] = 'c'; /* FIXME: need to set this correctly */
-	}
-
-	return [NSMethodSignature signatureWithObjCTypes:sig];
-}
-
-+ (PHPZval*)wrapZval:(zval*)value
-{
-	PHPZval *O = [PHPZval alloc];
-	O->val = value;
-	ZVAL_ADDREF(O->val);
-	return O;
-}
-@end
-/* }}} */
-
-static int is_implied_ref_param(const char *argtype)
-{
-	if (*argtype == _C_PTR) {
-		argtype++;
-		if (*argtype == _C_VOID) return 0;
-		return 1;
-	}
-	return 0;
-}
-
-static int populate_func_entry(zend_function_entry *fe,
-	SEL sel, const char *functype, int modifiers,
-	HashTable *dup_elim TSRMLS_DC)
-{
-	int narg;
-	char methname[1024];
-	char *cur;
-	struct objc_method fake_meth;
-
-	/* we want to determine the names of the args and their types.
-	 * We do the heavy lifting by faking a regular method entry
-	 * from the method description and using objc runtime provided
-	 * APIs. */
-	memset(&fake_meth, 0, sizeof(fake_meth));
-	fake_meth.method_name = sel;
-	fake_meth.method_types = (char*)functype;
-
-	/* TODO: skip over dangerous methods like release, retain */
-
-	memset(fe, 0, sizeof(*fe));
-	fe->num_args = method_getNumberOfArguments(&fake_meth) - 2;
-	fe->flags = modifiers;
-
-	if (fe->num_args) {
-		cur = (char*)sel_getName(sel);
-
-		/* the 0th slot is actually for the return value */
-		fe->arg_info = calloc(1 + fe->num_args, sizeof(zend_arg_info));
-		fe->arg_info[0].required_num_args = fe->num_args;
-
-		for (narg = 0; narg < fe->num_args; narg++) {
-			char *next = NULL;
-			int off;
+
+	/* now my favourite part, calling into PHP */
+	if (nargs) {
+		params = (zval***)safe_emalloc(sizeof(zval **), nargs, 0);
+		for (i = 0; i < nargs; i++) {
+			zval *arg = NULL;
 			const char *argtype;
-
-			/* we name the arguments based on the selector
-			 * name, with the ordinal position appended.
-			 * This is because methods like performSelector have
-			 * multiple withObject parameters. */
-
-			if (cur) {
-				if (*cur == ':') {
-					/* walk past : */
-					cur++;
-				}
-				next = strchr(cur, ':');
-			}
-
-			fe->arg_info[narg].name_len = spprintf(
-					&(fe->arg_info[narg+1].name), 0, "%.*s%d",
-					next - cur, cur, narg);
-
-			cur = next;
-
-			/* what about the type? */
-			method_getArgumentInfo(&fake_meth, narg+2, &argtype, &off);
-
+			int is_ref = 0;
+
+			argtype = [sig getArgumentTypeAtIndex:(i+2)];
+			params[i] = (zval**)emalloc(sizeof(zval**));
 			while (*argtype) {
 				if (*argtype == 'N' || *argtype == 'o' || *argtype == 'R') {
-					fe->arg_info[narg+1].pass_by_reference = 1;
+					is_ref = 1;
 					argtype++;
 					continue;
 				}
 				/* must be the actual type code now */
 				break;
 			}
-			if (!fe->arg_info[narg+1].pass_by_reference &&
+			if (!is_ref &&
 					is_implied_ref_param(argtype)) {
 				argtype++;
-				fe->arg_info[narg+1].pass_by_reference = 1;
+				is_ref = 1;
+			}
+
+			switch (*argtype) {
+				case _C_SEL: {
+					SEL s;
+					[inv getArgument:&s atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_STRING(arg, php_objc_SEL_to_string(s), 0);
+					break;
+				}
+				case _C_ID: {
+					id iarg = 0;
+
+					[inv getArgument:&iarg atIndex:(i+2)];
+
+					if (iarg && [iarg isKindOfClass:[PHPZval class]]) {
+						/* we get to ref the zval direct */
+						arg = [iarg _phpGetZval];
+						ZVAL_ADDREF(arg);
+					} else {
+						/* wrap it in */
+						ALLOC_INIT_ZVAL(arg);
+						php_objc_wrap_id(iarg, arg TSRMLS_CC);
+					}
+					break;
+				}
+				case _C_CHR: {
+					char val;
+					[inv getArgument:&val atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_LONG(arg, (long)val);
+					break;
+				}
+				case _C_UCHR: {
+					unsigned char val;
+					[inv getArgument:&val atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_LONG(arg, (long)val);
+					break;
+				}
+				case _C_SHT: {
+					short val;
+					[inv getArgument:&val atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_LONG(arg, (long)val);
+					break;
+				}
+				case _C_USHT: {
+					unsigned short val;
+					[inv getArgument:&val atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_LONG(arg, (long)val);
+					break;
+				}
+				case _C_INT: {
+					int val;
+					[inv getArgument:&val atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_LONG(arg, (long)val);
+					break;
+				}
+				case _C_UINT: {
+					unsigned int val;
+					[inv getArgument:&val atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_LONG(arg, (long)val);
+					break;
+				}
+				case _C_LNG: {
+					long val;
+					[inv getArgument:&val atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_LONG(arg, (long)val);
+					break;
+				}
+				case _C_ULNG: {
+					unsigned long val;
+					[inv getArgument:&val atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_LONG(arg, (long)val);
+					break;
+				}
+				case _C_FLT: {
+					float val;
+					[inv getArgument:&val atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_DOUBLE(arg, (double)val);
+					break;
+				}
+				case _C_DBL: {
+					double val;
+					[inv getArgument:&val atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_DOUBLE(arg, (double)val);
+					break;
+				}
+				case _C_BOOL: {
+					BOOL val;
+					[inv getArgument:&val atIndex:(i+2)];
+					ALLOC_INIT_ZVAL(arg);
+					ZVAL_BOOL(arg, val);
+					break;
+				}
+				default:
+					ALLOC_INIT_ZVAL(arg);
+			}
+			*params[i] = arg;
+		}
+	} else {
+		params = NULL;
+	}
+	zend_try {
+		if (SUCCESS == call_user_function_ex(&ce->function_table,
+					&zvthis, func_name, &retval, nargs, params, 1, NULL
+					TSRMLS_CC)) {
+		}
+	} zend_catch {
+	} zend_end_try();
+	if (params) {
+		for (i = 0; i < nargs; i++) {
+			zval_ptr_dtor(params[i]);
+			efree(params[i]);
+		}
+		efree(params);
+	}
+	if (EG(exception)) {
+		if (retval) {
+			zval_ptr_dtor(&retval);
+		}
+		zval_ptr_dtor(&func_name);
+		php_objc_raise(EG(exception) TSRMLS_CC);
+	}
+	if (retval) {
+		char *rettype = [[inv methodSignature] methodReturnType];
+					
+		if (*rettype != _C_VOID) {
+			switch (Z_TYPE_P(retval)) {
+				case IS_BOOL:
+				case IS_LONG:
+					{
+						long l = Z_LVAL_P(retval);
+						[inv setReturnValue:&l];
+						break;
+					}
+				case IS_NULL:
+					{
+						id null = 0;
+						[inv setReturnValue:&null];
+						break;
+					}
+				default:
+					{
+						id iret;
+						if (php_objc_mapping(retval, &iret, 0 TSRMLS_CC)) {
+							[inv setReturnValue:&iret];
+						}
+					}
+			}
+		}
+		zval_ptr_dtor(&retval);
+	}
+}
+
+@implementation PHPZval /* {{{ */
+- (void)dealloc
+{
+	if (val) {
+		zval_ptr_dtor(&val);
+	}
+	[super dealloc];
+}
+
+- (zval *)_phpGetZval
+{
+	return val;
+}
+
+- (BOOL)respondsToSelector:(SEL)aSelector
+{
+	struct objc_method_list *mlist;
+	void *iter = 0;
+
+	while ((mlist = class_nextMethodList(self->isa, &iter))) {
+		int i;
+		for (i = 0; i < mlist->method_count; i++) {
+			if (mlist->method_list[i].method_name == aSelector) {
+				return YES;
+			}
+		}
+	}
+
+	{
+		char *buf;
+		char *cur;
+		BOOL retval = NO;
+		zend_function *f;
+		zend_class_entry *ce;
+
+		buf = strdup(sel_getName(aSelector));
+		for (cur = buf; *cur; cur++) {
+			if (*cur == ':')
+				*cur = '_';
+			else
+				*cur = tolower(*cur);
+		}
+
+		ce = Z_OBJCE_P(self->val);
+		if (zend_hash_find(&ce->function_table,
+				buf, strlen(buf)+1, (void**)&f) == SUCCESS) {
+			retval = YES;
+		} else {
+			printf("I don't respond to %s\n", buf);
+		}
+
+		if (retval == NO) {
+			/* TODO: call a respondsToSelector method if present */
+		}
+
+		free(buf);
+		return retval;
+	}
+
+	return NO;
+}
+
+- (void)forwardInvocation:(NSInvocation *)anInvocation
+{
+	SEL sel = [anInvocation selector];
+
+	if (sel == @selector(respondsToSelector:)) {
+		BOOL b;
+		[anInvocation getArgument:&sel atIndex:2];
+		b = [self respondsToSelector:sel];
+		[anInvocation setReturnValue:&b];
+		return;
+	}
+
+	printf("I get to forward %s!\n", sel_getName(sel));
+	handle_method_invocation(anInvocation, self->val TSRMLS_CC);
+
+//	[super forwardInvocation:anInvocation];
+}
+
+- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
+{
+	char *sig;
+	struct objc_method *m;
+
+	m = class_getInstanceMethod(self->isa, aSelector);
+	if (m) {
+		sig = m->method_types;
+	} else {
+		const char *buf;
+		const char *cur;
+		int nargs = 0;
+
+		buf = sel_getName(aSelector);
+		for (cur = buf; *cur; cur++) {
+			if (*cur == ':') {
+				nargs++;
+			}
+		}
+
+		sig = alloca(nargs + 4);
+		memset(sig, '@', nargs + 3);
+		sig[nargs+3] = '\0';
+		sig[2] = ':';
+		sig[0] = 'c'; /* FIXME: need to set this correctly */
+	}
+
+	return [NSMethodSignature signatureWithObjCTypes:sig];
+}
+
++ (PHPZval*)wrapZval:(zval*)value
+{
+	PHPZval *O = [PHPZval alloc];
+	O->val = value;
+	ZVAL_ADDREF(O->val);
+	return O;
+}
+@end
+/* }}} */
+
+int is_implied_ref_param(const char *argtype)
+{
+	if (*argtype == _C_PTR) {
+		argtype++;
+		if (*argtype == _C_VOID) return 0;
+		return 1;
+	}
+	return 0;
+}
+
+static int populate_func_entry(zend_function_entry *fe,
+	SEL sel, const char *functype, int modifiers,
+	HashTable *dup_elim TSRMLS_DC)
+{
+	int narg;
+	char methname[1024];
+	char *cur;
+	struct objc_method fake_meth;
+
+	/* we want to determine the names of the args and their types.
+	 * We do the heavy lifting by faking a regular method entry
+	 * from the method description and using objc runtime provided
+	 * APIs. */
+	memset(&fake_meth, 0, sizeof(fake_meth));
+	fake_meth.method_name = sel;
+	fake_meth.method_types = (char*)functype;
+
+	/* TODO: skip over dangerous methods like release, retain */
+
+	memset(fe, 0, sizeof(*fe));
+	fe->num_args = method_getNumberOfArguments(&fake_meth) - 2;
+	fe->flags = modifiers;
+
+	if (fe->num_args) {
+		zend_arg_info *arg_info;
+
+		cur = (char*)sel_getName(sel);
+
+		/* the 0th slot is actually for the return value */
+		arg_info = calloc(1 + fe->num_args, sizeof(zend_arg_info));
+		arg_info[0].required_num_args = fe->num_args;
+		fe->arg_info = arg_info;
+
+		for (narg = 0; narg < fe->num_args; narg++) {
+			char *next = NULL;
+			int off;
+			const char *argtype;
+
+			/* we name the arguments based on the selector
+			 * name, with the ordinal position appended.
+			 * This is because methods like performSelector have
+			 * multiple withObject parameters. */
+
+			if (cur) {
+				if (*cur == ':') {
+					/* walk past : */
+					cur++;
+				}
+				next = strchr(cur, ':');
+			}
+
+			arg_info[narg].name_len = spprintf(
+					&(fe->arg_info[narg+1].name), 0, "%.*s%d",
+					next - cur, cur, narg);
+
+			cur = next;
+
+			/* what about the type? */
+			method_getArgumentInfo(&fake_meth, narg+2, &argtype, &off);
+
+			while (*argtype) {
+				if (*argtype == 'N' || *argtype == 'o' || *argtype == 'R') {
+					arg_info[narg+1].pass_by_reference = 1;
+					argtype++;
+					continue;
+				}
+				if (*argtype == 'r' || *argtype == 'n' || *argtype == 'O'
+						|| *argtype == 'V') {
+					argtype++;
+					continue;
+				}
+				/* must be the actual type code now */
+				break;
+			}
+			if (!arg_info[narg+1].pass_by_reference &&
+					is_implied_ref_param(argtype)) {
+				argtype++;
+				arg_info[narg+1].pass_by_reference = 1;
 			}
 			while (*argtype) {
 				if (*argtype == 'r' || *argtype == 'n' || *argtype == 'O'
 					goto cant_map;
 
 				default:
-					printf("unhandled arg type %s for method %s\n", argtype, sel_getName(sel));
+					printf("unhandled arg type %s for method %s\n",
+							argtype, sel_getName(sel));
 					exit(0);
 			}
 		}
 }
 
 /* Make sure we aren't defining methods that we'll pick up via inheritance */
-static void fix_ftable(zend_function_entry *ftable, zend_class_entry *ce TSRMLS_DC)
+static void fix_ftable(zend_function_entry *ftable,
+	zend_class_entry *ce TSRMLS_DC)
 {
 	int ftable_size = 0;
 	int i;
 					b->arKey, b->nKeyLength-1, (void**)&findme)) {
 			/* already in there */
 		} else {
+			zend_arg_info *arg_info;
 
 			ftable->fname = strdup(f->common.function_name);
 			ftable->flags = f->common.fn_flags & ~ZEND_ACC_ABSTRACT;
 			ftable->num_args = f->common.num_args;
-			ftable->arg_info = calloc(f->common.num_args + 1,
+			arg_info = calloc(f->common.num_args + 1,
 					sizeof(zend_arg_info));
-			ftable->arg_info[0].required_num_args = f->common.num_args;
-			memcpy(ftable->arg_info + 1,
+			ftable->arg_info = arg_info;
+			arg_info[0].required_num_args = f->common.num_args;
+			memcpy(arg_info + 1,
 					f->common.arg_info,
 					f->common.num_args * sizeof(zend_arg_info));
 
 
 static php_objc_class *php_objc_import_class(const char *name TSRMLS_DC)
 {
-	php_objc_class c, *cls, *parent_cls;
+	php_objc_class c, *cls, *parent_cls = NULL;
 	zend_class_entry ce;
 	struct objc_protocol_list *plist;
 	void *iter = 0;
 	return SUCCESS;
 }
 
+static PHP_FUNCTION(objc_import_framework)
+{
+	char *name = NULL;
+	int namelen;
+	NSAutoreleasePool *p;
+
+	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+			"s", &name, &namelen)) {
+		RETURN_FALSE;
+	}
+
+	p = [[NSAutoreleasePool alloc] init];
+	php_objc_load_framework(name TSRMLS_CC);
+	[p release];
+}
+
 static PHP_FUNCTION(objc_load_bundle)
 {
 	char *name = NULL;
 	zend_hash_internal_pointer_reset_ex(EG(class_table), &pos);
 	do {
 		zend_class_entry **pce;
-		if (FAILURE == zend_hash_get_current_data_ex(EG(class_table), (void**)&pce, &pos)) {
+		if (FAILURE == zend_hash_get_current_data_ex(EG(class_table),
+				(void**)&pce, &pos)) {
 			break;
 		}
 		php_objc_export_class(*pce TSRMLS_CC);
 	i = 0;
 	do {
 		zval **zv;
-		if (FAILURE == zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void**)&zv, &pos)) {
+		if (FAILURE == zend_hash_get_current_data_ex(Z_ARRVAL_P(arr),
+				(void**)&zv, &pos)) {
 			break;
 		}
 		convert_to_string_ex(zv);
 	/* TODO: objc_getClassList */
 	PHP_FE(objc_import_class, NULL)
 	PHP_FE(objc_load_bundle, NULL)
+	PHP_FE(objc_import_framework, NULL)
 	PHP_FE(objc_export_class, NULL)
 	PHP_FE(objc_new, NULL)
 	PHP_FE(NSApplicationMain, NULL)
 }
 
 /* adds a ref to the underlying sel for the caller */
-static int php_objc_mapping(zval *object, id *idval, int throw TSRMLS_DC)
+int php_objc_mapping(zval *object, id *idval, int throw TSRMLS_DC)
 {
 	switch (Z_TYPE_P(object)) {
 		case IS_OBJECT: {
 static zval *php_objc_prop_read(zval *object, zval *member, int type TSRMLS_DC)
 {
 	return std_object_handlers.read_property(object, member, type TSRMLS_CC);
-#if 0
-	zval *return_value;
-
-	MAKE_STD_ZVAL(return_value);
-	ZVAL_NULL(return_value);
-	return_value->refcount = 0;
-	return_value->is_ref = 0;
-	return return_value;
-#endif
 }
 
-static void php_objc_prop_write(zval *object, zval *member, zval *value TSRMLS_DC)
+static void php_objc_prop_write(zval *object, zval *member,
+	zval *value TSRMLS_DC)
 {
 	std_object_handlers.write_property(object, member, value TSRMLS_CC);
-#if 0
-	php_objc_throw("property write not supported" TSRMLS_CC);
-#endif
 }
 
 static zval *php_objc_dim_read(zval *object, zval *offset, int type TSRMLS_DC)
 
 	MAKE_STD_ZVAL(return_value);
 	ZVAL_NULL(return_value);
+#ifdef Z_UNSET_ISREF_P
+	Z_UNSET_ISREF_P(return_value);
+#else
+	return_value->is_ref = 0;
+#endif
+#ifdef Z_SET_REFCOUNT_P
+	Z_SET_REFCOUNT_P(return_value, 0);
+#else
 	return_value->refcount = 0;
-	return_value->is_ref = 0;
+#endif
 
 NS_DURING
 	if ([obj->instance isKindOfClass:[NSArray class]]) {
 
 		val = [obj->instance valueForKey:str];
 
-		[str dealloc];
+		[str release];
 	}
 
 	if (val) {
 	return return_value;
 }
 
-static void php_objc_dim_write(zval *object, zval *offset, zval *value TSRMLS_DC)
+static void php_objc_dim_write(zval *object, zval *offset,
+	zval *value TSRMLS_DC)
 {
 	id val = 0;
 	php_objc_obj *obj = PHPOBJC(object);
 NS_ENDHANDLER
 }
 
-static int php_objc_prop_exists(zval *object, zval *member, int has_set_exists TSRMLS_DC)
+static int php_objc_prop_exists(zval *object, zval *member,
+	int has_set_exists TSRMLS_DC)
 {
-	return std_object_handlers.has_property(object, member, has_set_exists TSRMLS_CC);;
+	return std_object_handlers.has_property(object, member,
+		has_set_exists TSRMLS_CC);;
 }
 
 static void php_objc_prop_del(zval *object, zval *member TSRMLS_DC)
 	return std_object_handlers.unset_property(object, member TSRMLS_CC);
 }
 
-static int php_objc_dim_exists(zval *object, zval *member, int chk_empty TSRMLS_DC)
+static int php_objc_dim_exists(zval *object, zval *member,
+	int chk_empty TSRMLS_DC)
 {
 	id val = 0;
 	php_objc_obj *obj = PHPOBJC(object);
 
 		[str dealloc];
 	} else {
-		val = std_object_handlers.has_dimension(object, member, chk_empty TSRMLS_CC);
+		val = std_object_handlers.has_dimension(object, member,
+				chk_empty TSRMLS_CC);
 	}
 
 	if (val) {
 	return buf;
 }
 
-static SEL php_objc_method_name_to_SEL(const char *name, int len)
+SEL php_objc_method_name_to_SEL(const char *name, int len)
 {
 	char buf[1024];
 	char *cur;
 	return NULL;
 }
 			
-static void php_objc_wrap_id(id idval, zval *return_value)
+void php_objc_wrap_id(id idval, zval *return_value)
 {
 	php_objc_obj *nobj;
 	php_objc_class *cls;
 
+	if (idval == 0) {
+		ZVAL_NULL(return_value);
+		return;
+	}
+
 	if ([[idval class] conformsToProtocol:@protocol(PHPHasZval)]) {
 		zval *zv = [idval _phpGetZval];
 
 		return;
 	}
 
-	php_objc_method_call_inner(method_name, 1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+	php_objc_method_call_inner(method_name, 1,
+		INTERNAL_FUNCTION_PARAM_PASSTHRU);
 }
 
 static PHP_FUNCTION(objc_alloc)
 
 static int php_objc_method_call(char *method, INTERNAL_FUNCTION_PARAMETERS)
 {
-	return php_objc_method_call_inner(method, 0, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+	return php_objc_method_call_inner(method, 0,
+		INTERNAL_FUNCTION_PARAM_PASSTHRU);
 }
 
-static int php_objc_method_call_inner(char *method, int skip_arg_static, INTERNAL_FUNCTION_PARAMETERS)
+static int php_objc_method_call_inner(char *method, int skip_arg_static,
+	INTERNAL_FUNCTION_PARAMETERS)
 {
 	zval ***args = NULL;
 	int nargs, i;
 	ffi_arg_types = calloc(2 + nargs_passed, sizeof(ffi_type*));
 	ffi_args = calloc(2 + nargs_passed, sizeof(void*));
 
-	if (obj && obj->from_objc && EG(active_op_array) && EG(active_op_array)->scope == cls->ce) {
+	if (obj && obj->from_objc && EG(active_op_array) &&
+			EG(active_op_array)->scope == cls->ce) {
 		sup.receiver = obj->instance;
 		sup.class = cls->class_id->super_class;
 		supptr = &sup;
 		}
 #endif
 		case FFI_TYPE_STRUCT:
-			php_objc_throw("structure return types are not currently supported" TSRMLS_CC);
+			php_objc_throw(
+				"structure return types are not currently supported" TSRMLS_CC);
 			break;
 		default: {
 			union {
 		}
 	}
 
-
-
-#if 0
-
-		default: {
-			/* simple */
-			id retval = objc_msgSendv(
-				obj ? obj->instance : cls->class_id,
-				sel,
-				frame_size, mlist);
-
-printf("retval is %p\n", retval);
-	
-			switch (*argtype) {
-#ifndef __i386__
-				case _C_FLT:
-   					RETVAL_DOUBLE((double)(float)retval);
-					break;
-				case _C_DBL: 
-   					RETVAL_DOUBLE((double)retval);
-					break;
-#endif
-				case _C_SEL:
-   					RETVAL_STRING((char*)sel_getName((SEL)retval), 1);
-					break;
-				case _C_CLASS:
-					RETVAL_STRING((char*)((Class)retval)->name, 1);
-					break;
-				case _C_CHR:
-				case _C_UCHR:
-   					RETVAL_LONG((long)(char)(intptr_t)retval);
-					break;
-				case _C_SHT:
-				case _C_USHT:
-   					RETVAL_LONG((long)(short)(intptr_t)retval);
-					break;
-				case _C_UINT:
-				case _C_INT:
-					RETVAL_LONG((long)(unsigned)retval);
-					break;
-				case _C_ULNG:
-				case _C_LNG:
-					RETVAL_LONG((long)retval);
-					break;
-				case _C_ID:
-					if (retval) {
-						php_objc_wrap_id(retval, return_value);
-					} else {
-						RETVAL_NULL();
-					}
-					break;
-				case _C_CHARPTR:
-					RETVAL_STRING((char*)retval, 1);
-					break;
-				case _C_VOID:
-					/* void! */
-					break;
-
-				default:
-					php_objc_throw("unhandled ret type %s\n" TSRMLS_CC,
-						argtype);
-			}
-		}
-#endif
-
 	if (byref_hash) {
 		/* now fixup by-ref parameters */
 		for (i = 0; i < nargs; i++) {
 					break;
 				}
 
-#if 0
-				case _C_CHARPTR: {
-									 char *str = marg_getValue(mlist, offset, char*);
-									 ZVAL_STRING(*args[i + first_passed], str, 1);
-									 break;
-								 }
-				case _C_SEL: {
-								 SEL s = marg_getValue(mlist, offset, SEL);
-								 char *str = sel_getName(s);
-								 ZVAL_STRING(*args[i + first_passed], str, 1);
-								 break;
-							 }
-				case _C_CLASS: {
-								   Class c = marg_getValue(mlist, offset, Class);
-								   char *str = c->name;
-								   ZVAL_STRING(*args[i + first_passed], str, 1);
-								   break;
-							   }
-				case _C_STRUCT_B: {
-									  php_objc_throw(
-											  "references to structures are not currently supported"
-											  TSRMLS_CC);
-									  return;