/* Compiler implementation of the D programming language * Copyright (c) 2015 by Digital Mars * All Rights Reserved * written by Michel Fortin * http://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * http://www.boost.org/LICENSE_1_0.txt * https://github.com/dlang/dmd/blob/master/src/_objc_glue.d */ module ddmd.objc_glue; import core.stdc.stdio; import core.stdc.stdlib; import core.stdc.string; import ddmd.aggregate; import ddmd.declaration; import ddmd.dmodule; import ddmd.func; import ddmd.identifier; import ddmd.mars; import ddmd.mtype; import ddmd.objc; import ddmd.root.stringtable; import ddmd.backend.dt; import ddmd.backend.cc; import ddmd.backend.cdef; import ddmd.backend.el; import ddmd.backend.global; import ddmd.backend.oper; import ddmd.backend.outbuf; import ddmd.backend.ty; import ddmd.backend.type; import ddmd.backend.mach; import ddmd.backend.obj; extern (C++): enum ObjcSegment { SEGcstring, SEGimage_info, SEGmethname, SEGmodule_info, SEGselrefs, SEG_MAX, } elem *addressElem(elem *e, Type t, bool alwaysCopy = false); __gshared int[ObjcSegment.SEG_MAX] objc_segList; int objc_getSegment(ObjcSegment segid) { int *seg = objc_segList.ptr; if (seg[segid] != 0) return seg[segid]; // initialize int _align = 3; with (ObjcSegment) { seg[SEGcstring] = MachObj.getsegment("__cstring", "__TEXT", _align, S_CSTRING_LITERALS); seg[SEGimage_info] = MachObj.getsegment("__objc_imageinfo", "__DATA", _align, S_REGULAR | S_ATTR_NO_DEAD_STRIP); seg[SEGmethname] = MachObj.getsegment("__objc_methname", "__TEXT", _align, S_CSTRING_LITERALS); seg[SEGmodule_info] = MachObj.getsegment("__objc_classlist", "__DATA", _align, S_REGULAR | S_ATTR_NO_DEAD_STRIP); seg[SEGselrefs] = MachObj.getsegment("__objc_selrefs", "__DATA", _align, S_ATTR_NO_DEAD_STRIP | S_LITERAL_POINTERS); } return seg[segid]; } // MARK: ObjcSymbols __gshared { bool objc_hasSymbols = false; Symbol *objc_smsgSend = null; Symbol *objc_smsgSend_stret = null; Symbol *objc_smsgSend_fpret = null; Symbol *objc_smsgSend_fp2ret = null; Symbol *objc_simageInfo = null; Symbol *objc_smoduleInfo = null; StringTable *objc_smethVarNameTable = null; StringTable *objc_smethVarRefTable = null; } static StringTable *initStringTable(StringTable *stringtable) { stringtable = new StringTable(); stringtable._init(); return stringtable; } void objc_initSymbols() { objc_hasSymbols = false; objc_smsgSend = null; objc_smsgSend_stret = null; objc_smsgSend_fpret = null; objc_smsgSend_fp2ret = null; objc_simageInfo = null; objc_smoduleInfo = null; // clear tables objc_smethVarNameTable = initStringTable(objc_smethVarNameTable); objc_smethVarRefTable = initStringTable(objc_smethVarRefTable); // also wipe out segment numbers for (int s = 0; s < ObjcSegment.SEG_MAX; ++s) objc_segList[s] = 0; } Symbol *objc_getCString(const(char)* str, size_t len, const(char)* symbolName, ObjcSegment segment) { objc_hasSymbols = true; // create data scope dtb = new DtBuilder(); dtb.nbytes(cast(uint)(len + 1), str); // find segment int seg = objc_getSegment(segment); // create symbol Symbol *s; s = symbol_name(symbolName, SCstatic, type_allocn(TYarray, tstypes[TYchar])); s.Sdt = dtb.finish(); s.Sseg = seg; return s; } Symbol *objc_getMethVarName(const(char)* s, size_t len) { objc_hasSymbols = true; StringValue *sv = objc_smethVarNameTable.update(s, len); Symbol *sy = cast(Symbol *) sv.ptrvalue; if (!sy) { __gshared size_t classnamecount = 0; char[42] namestr; sprintf(namestr.ptr, "L_OBJC_METH_VAR_NAME_%lu", classnamecount++); sy = objc_getCString(s, len, namestr.ptr, ObjcSegment.SEGmethname); sv.ptrvalue = sy; } return sy; } Symbol *objc_getMethVarName(Identifier *ident) { const char* id = ident.toChars(); return objc_getMethVarName(id, strlen(id)); } Symbol *objc_getMsgSend(Type ret, bool hasHiddenArg) { if (hasHiddenArg) { if (!objc_smsgSend_stret) objc_smsgSend_stret = symbol_name("_objc_msgSend_stret", SCglobal, type_fake(TYhfunc)); return objc_smsgSend_stret; } // not sure if DMD can handle this else if (ret.ty == Tcomplex80) { if (!objc_smsgSend_fp2ret) objc_smsgSend_fp2ret = symbol_name("_objc_msgSend_fp2ret", SCglobal, type_fake(TYnfunc)); return objc_smsgSend_fp2ret; } else if (ret.ty == Tfloat80) { if (!objc_smsgSend_fpret) objc_smsgSend_fpret = symbol_name("_objc_msgSend_fpret", SCglobal, type_fake(TYnfunc)); return objc_smsgSend_fpret; } else { if (!objc_smsgSend) objc_smsgSend = symbol_name("_objc_msgSend", SCglobal, type_fake(TYnfunc)); return objc_smsgSend; } assert(0); } Symbol *objc_getImageInfo() { if (objc_simageInfo) return objc_simageInfo; objc_hasSymbols = true; scope dtb = new DtBuilder(); dtb.dword(0); // version dtb.dword(0); // flags objc_simageInfo = symbol_name("L_OBJC_IMAGE_INFO", SCstatic, type_allocn(TYarray, tstypes[TYchar])); objc_simageInfo.Sdt = dtb.finish(); objc_simageInfo.Sseg = objc_getSegment(ObjcSegment.SEGimage_info); outdata(objc_simageInfo); return objc_simageInfo; } Symbol *objc_getModuleInfo() { assert(!objc_smoduleInfo); // only allow once per object file objc_hasSymbols = true; scope dtb = new DtBuilder(); Symbol* symbol = symbol_name("L_OBJC_LABEL_CLASS_$", SCstatic, type_allocn(TYarray, tstypes[TYchar])); symbol.Sdt = dtb.finish(); symbol.Sseg = objc_getSegment(ObjcSegment.SEGmodule_info); outdata(symbol); objc_getImageInfo(); // make sure we also generate image info return objc_smoduleInfo; } // MARK: Module.genmoduleinfo void objc_Module_genmoduleinfo_classes() { if (objc_hasSymbols) objc_getModuleInfo(); } // MARK: ObjcSelector Symbol *objc_getMethVarRef(const(char)* s, size_t len) { objc_hasSymbols = true; StringValue *sv = objc_smethVarRefTable.update(s, len); Symbol *refsymbol = cast(Symbol *) sv.ptrvalue; if (refsymbol == null) { // create data scope dtb = new DtBuilder(); Symbol *sselname = objc_getMethVarName(s, len); dtb.xoff(sselname, 0, TYnptr); // find segment int seg = objc_getSegment(ObjcSegment.SEGselrefs); // create symbol __gshared size_t selcount = 0; char[42] namestr; sprintf(namestr.ptr, "L_OBJC_SELECTOR_REFERENCES_%lu", selcount); refsymbol = symbol_name(namestr.ptr, SCstatic, type_fake(TYnptr)); refsymbol.Sdt = dtb.finish(); refsymbol.Sseg = seg; outdata(refsymbol); sv.ptrvalue = refsymbol; ++selcount; } return refsymbol; } Symbol *objc_getMethVarRef(Identifier ident) { auto id = ident.toChars(); return objc_getMethVarRef(id, strlen(id)); } // MARK: callfunc void objc_callfunc_setupMethodSelector(Type tret, FuncDeclaration fd, Type t, elem *ehidden, elem **esel) { if (fd && fd.objc.selector && !*esel) { *esel = el_var(objc_getMethVarRef(fd.objc.selector.stringvalue, fd.objc.selector.stringlen)); } } void objc_callfunc_setupMethodCall(elem **ec, elem *ehidden, elem *ethis, TypeFunction tf) { // make objc-style "virtual" call using dispatch function assert(ethis); Type tret = tf.next; *ec = el_var(objc_getMsgSend(tret, ehidden !is null)); } void objc_callfunc_setupEp(elem *esel, elem **ep, int reverse) { if (esel) { // using objc-style "virtual" call // add hidden argument (second to 'this') for selector used by dispatch function if (reverse) *ep = el_param(esel,*ep); else *ep = el_param(*ep,esel); } }