#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "output.h"
#include "flags.h"
#include "tree.h"
#include "expr.h"
#include "tm_p.h"
#include "cp/cp-tree.h"
#include "toplev.h"
#define SYMBIAN_DEBUG 0
#define SH_SYMBIAN_FLAG_CHAR "$"
#define DLL_IMPORT_PREFIX SH_SYMBIAN_FLAG_CHAR "i."
#define DLL_EXPORT_PREFIX SH_SYMBIAN_FLAG_CHAR "e."
static tree
sh_symbian_associated_type (tree decl)
{
tree t = NULL_TREE;
if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
return t;
if (DECL_CONTEXT (decl) && TYPE_P (DECL_CONTEXT (decl)))
t = DECL_CONTEXT (decl);
return t;
}
bool
sh_symbian_dllexport_p (tree decl)
{
tree exp;
if ( TREE_CODE (decl) != VAR_DECL
&& TREE_CODE (decl) != FUNCTION_DECL)
return false;
exp = lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl));
if (exp == NULL)
{
tree class = sh_symbian_associated_type (decl);
if (class)
exp = lookup_attribute ("dllexport", TYPE_ATTRIBUTES (class));
}
#if SYMBIAN_DEBUG
if (exp)
{
print_node_brief (stderr, "dllexport:", decl, 0);
fprintf (stderr, "\n");
}
else
#if SYMBIAN_DEBUG < 2
if (TREE_CODE (decl) != FUNCTION_DECL)
#endif
{
print_node_brief (stderr, "no dllexport:", decl, 0);
fprintf (stderr, "\n");
}
#endif
return exp ? true : false;
}
static bool
sh_symbian_dllimport_p (tree decl)
{
tree imp;
if ( TREE_CODE (decl) != VAR_DECL
&& TREE_CODE (decl) != FUNCTION_DECL)
return false;
imp = lookup_attribute ("dllimport", DECL_ATTRIBUTES (decl));
if (imp)
return true;
imp = sh_symbian_associated_type (decl);
if (! imp)
return false;
imp = lookup_attribute ("dllimport", TYPE_ATTRIBUTES (imp));
if (!imp)
return false;
if (TREE_CODE (decl) == FUNCTION_DECL
&& DECL_INITIAL (decl)
&& !DECL_INLINE (decl))
{
if (!DECL_ARTIFICIAL (decl))
warning ("%H function '%D' is defined after prior declaration as dllimport: attribute ignored",
& DECL_SOURCE_LOCATION (decl), decl);
return false;
}
else if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INLINE (decl))
{
if (extra_warnings)
warning ("%Hinline function '%D' is declared as dllimport: attribute ignored.",
& DECL_SOURCE_LOCATION (decl), decl);
return false;
}
else if (TREE_CODE (decl) == VAR_DECL
&& TREE_STATIC (decl)
&& TREE_PUBLIC (decl)
&& !DECL_EXTERNAL (decl))
{
if (!DECL_VIRTUAL_P (decl))
error ("%Hdefinition of static data member '%D' of dllimport'd class.",
& DECL_SOURCE_LOCATION (decl), decl);
return false;
}
else if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE
&& (DECL_VIRTUAL_P (decl) || DECL_ARTIFICIAL (decl)))
return false;
return true;
}
bool
sh_symbian_dllexport_name_p (const char *symbol)
{
return strncmp (DLL_EXPORT_PREFIX, symbol,
strlen (DLL_EXPORT_PREFIX)) == 0;
}
bool
sh_symbian_dllimport_name_p (const char *symbol)
{
return strncmp (DLL_IMPORT_PREFIX, symbol,
strlen (DLL_IMPORT_PREFIX)) == 0;
}
static void
sh_symbian_mark_dllexport (tree decl)
{
const char *oldname;
char *newname;
rtx rtlname;
tree idp;
rtlname = XEXP (DECL_RTL (decl), 0);
if (GET_CODE (rtlname) == SYMBOL_REF)
oldname = XSTR (rtlname, 0);
else if (GET_CODE (rtlname) == MEM
&& GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF)
oldname = XSTR (XEXP (rtlname, 0), 0);
else
abort ();
if (sh_symbian_dllimport_name_p (oldname))
{
oldname += strlen (DLL_IMPORT_PREFIX);
DECL_NON_ADDR_CONST_P (decl) = 0;
}
else if (sh_symbian_dllexport_name_p (oldname))
return;
newname = alloca (strlen (DLL_EXPORT_PREFIX) + strlen (oldname) + 1);
sprintf (newname, "%s%s", DLL_EXPORT_PREFIX, oldname);
idp = get_identifier (newname);
XEXP (DECL_RTL (decl), 0) =
gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp));
}
static void
sh_symbian_mark_dllimport (tree decl)
{
const char *oldname;
char *newname;
tree idp;
rtx rtlname;
rtx newrtl;
rtlname = XEXP (DECL_RTL (decl), 0);
if (GET_CODE (rtlname) == SYMBOL_REF)
oldname = XSTR (rtlname, 0);
else if (GET_CODE (rtlname) == MEM
&& GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF)
oldname = XSTR (XEXP (rtlname, 0), 0);
else
abort ();
if (sh_symbian_dllexport_name_p (oldname))
{
error ("%qs declared as both exported to and imported from a DLL",
IDENTIFIER_POINTER (DECL_NAME (decl)));
}
else if (sh_symbian_dllimport_name_p (oldname))
{
if (!DECL_EXTERNAL (decl) || !TREE_PUBLIC (decl))
error ("%Hfailure in redeclaration of '%D': dllimport'd symbol lacks external linkage.",
&DECL_SOURCE_LOCATION (decl), decl);
}
else
{
newname = alloca (strlen (DLL_IMPORT_PREFIX) + strlen (oldname) + 1);
sprintf (newname, "%s%s", DLL_IMPORT_PREFIX, oldname);
idp = get_identifier (newname);
newrtl = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp));
XEXP (DECL_RTL (decl), 0) = newrtl;
}
}
void
sh_symbian_encode_section_info (tree decl, rtx rtl, int first)
{
default_encode_section_info (decl, rtl, first);
if (sh_symbian_dllexport_p (decl))
sh_symbian_mark_dllexport (decl);
else if (sh_symbian_dllimport_p (decl))
sh_symbian_mark_dllimport (decl);
else if ( (TREE_CODE (decl) == FUNCTION_DECL
|| TREE_CODE (decl) == VAR_DECL)
&& DECL_RTL (decl) != NULL_RTX
&& GET_CODE (DECL_RTL (decl)) == MEM
&& GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM
&& GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 0)) == SYMBOL_REF
&& sh_symbian_dllimport_name_p (XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0)))
{
const char * oldname = XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0);
tree idp = get_identifier (oldname + strlen (DLL_IMPORT_PREFIX));
rtx newrtl = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp));
warning ("%H%s '%D' %s after being referenced with dllimport linkage.",
& DECL_SOURCE_LOCATION (decl),
TREE_CODE (decl) == VAR_DECL ? "variable" : "function",
decl, (DECL_INITIAL (decl) || !DECL_EXTERNAL (decl))
? "defined locally" : "redeclared without dllimport attribute");
XEXP (DECL_RTL (decl), 0) = newrtl;
DECL_NON_ADDR_CONST_P (decl) = 0;
}
}
static int
sh_symbian_get_strip_length (int c)
{
return (c == SH_SYMBIAN_FLAG_CHAR[0]) ? strlen (DLL_EXPORT_PREFIX) : 0;
}
const char *
sh_symbian_strip_name_encoding (const char *name)
{
int skip;
while ((skip = sh_symbian_get_strip_length (*name)))
name += skip;
return name;
}
static void
symbian_add_attribute (tree node, const char *attr_name)
{
tree attrs;
tree attr;
attrs = DECL_P (node) ? DECL_ATTRIBUTES (node) : TYPE_ATTRIBUTES (node);
if (lookup_attribute (attr_name, attrs) != NULL_TREE)
return;
attr = get_identifier (attr_name);
(DECL_P (node) ? DECL_ATTRIBUTES (node) : TYPE_ATTRIBUTES (node))
= tree_cons (attr, NULL_TREE, attrs);
#if SYMBIAN_DEBUG
fprintf (stderr, "propogate %s attribute", attr_name);
print_node_brief (stderr, " to", node, 0);
fprintf (stderr, "\n");
#endif
}
tree
sh_symbian_handle_dll_attribute (tree *pnode, tree name, tree args,
int flags, bool *no_add_attrs)
{
tree thunk;
tree node = *pnode;
const char *attr = IDENTIFIER_POINTER (name);
if (!DECL_P (node))
{
if (flags & ((int) ATTR_FLAG_DECL_NEXT
| (int) ATTR_FLAG_FUNCTION_NEXT
| (int) ATTR_FLAG_ARRAY_NEXT))
{
warning ("%qs attribute ignored", attr);
*no_add_attrs = true;
return tree_cons (name, args, NULL_TREE);
}
if (TREE_CODE (node) != RECORD_TYPE && TREE_CODE (node) != UNION_TYPE)
{
warning ("%qs attribute ignored", attr);
*no_add_attrs = true;
}
return NULL_TREE;
}
else if (is_attribute_p ("dllimport", name))
{
if (TREE_CODE (node) == VAR_DECL)
{
if (DECL_INITIAL (node))
{
error ("%Hvariable %qD definition is marked dllimport.",
& DECL_SOURCE_LOCATION (node), node);
*no_add_attrs = true;
}
DECL_EXTERNAL (node) = 1;
if (current_function_decl != NULL_TREE && ! TREE_STATIC (node))
TREE_PUBLIC (node) = 1;
}
}
if (TREE_CODE (TREE_TYPE (node)) == METHOD_TYPE
&& (DECL_CONSTRUCTOR_P (node) || DECL_DESTRUCTOR_P (node)))
{
tree overload;
for (overload = OVL_CHAIN (node); overload; overload = OVL_CHAIN (overload))
{
tree node_args;
tree func_args;
tree function = OVL_CURRENT (overload);
if (! function
|| ! DECL_P (function)
|| (DECL_CONSTRUCTOR_P (node) && ! DECL_CONSTRUCTOR_P (function))
|| (DECL_DESTRUCTOR_P (node) && ! DECL_DESTRUCTOR_P (function)))
continue;
for (node_args = DECL_ARGUMENTS (node), func_args = DECL_ARGUMENTS (function);
node_args && func_args;
node_args = TREE_CHAIN (node_args), func_args = TREE_CHAIN (func_args))
if (TREE_TYPE (node_args) != TREE_TYPE (func_args))
break;
if (node_args || func_args)
{
if ((node_args == NULL_TREE
|| func_args != NULL_TREE)
&& strcmp (IDENTIFIER_POINTER (DECL_NAME (node)), "__in_chrg") != 0)
continue;
}
symbian_add_attribute (function, attr);
for (thunk = DECL_THUNKS (function); thunk; thunk = TREE_CHAIN (thunk))
if (TREE_CODE (thunk) == FUNCTION_DECL)
symbian_add_attribute (thunk, attr);
}
}
if (TREE_CODE (node) == FUNCTION_DECL && DECL_VIRTUAL_P (node))
{
for (thunk = DECL_THUNKS (node); thunk; thunk = TREE_CHAIN (thunk))
if (TREE_CODE (thunk) == FUNCTION_DECL)
symbian_add_attribute (thunk, attr);
}
if (!TREE_PUBLIC (node)
&& ( TREE_CODE (node) == VAR_DECL
|| TREE_CODE (node) == FUNCTION_DECL))
{
error ("%Hexternal linkage required for symbol '%D' because of '%s' attribute.",
& DECL_SOURCE_LOCATION (node), node, IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
#if SYMBIAN_DEBUG
print_node_brief (stderr, "mark node", node, 0);
fprintf (stderr, " as %s\n", attr);
#endif
return NULL_TREE;
}
static void
symbian_possibly_export_base_class (tree base_class)
{
tree methods;
int len;
if (! (TYPE_CONTAINS_VPTR_P (base_class)))
return;
methods = CLASSTYPE_METHOD_VEC (base_class);
len = methods ? TREE_VEC_LENGTH (methods) : 0;
for (;len --;)
{
tree member = TREE_VEC_ELT (methods, len);
if (! member)
continue;
for (member = OVL_CURRENT (member); member; member = OVL_NEXT (member))
{
if (TREE_CODE (member) != FUNCTION_DECL)
continue;
if (DECL_CONSTRUCTOR_P (member) || DECL_DESTRUCTOR_P (member))
continue;
if (! DECL_VIRTUAL_P (member))
continue;
if (DECL_PURE_VIRTUAL_P (member))
continue;
if (DECL_INLINE (member))
continue;
break;
}
if (member)
break;
}
if (len < 0)
return;
#if SYMBIAN_DEBUG
print_node_brief (stderr, "", base_class, 0);
fprintf (stderr, " EXPORTed [base class of exported class]\n");
#endif
}
static bool
symbian_export_vtable_and_rtti_p (tree ctype)
{
bool inline_ctor_dtor;
bool dllimport_ctor_dtor;
bool dllimport_member;
tree binfo, base_binfo;
tree methods;
tree key;
int i;
int len;
if (TREE_CODE (ctype) != RECORD_TYPE)
{
#if SYMBIAN_DEBUG
print_node_brief (stderr, "", ctype, 0);
fprintf (stderr, " does NOT need to be EXPORTed [not a class]\n");
#endif
return false;
}
if ((key = CLASSTYPE_KEY_METHOD (ctype)) == NULL_TREE)
{
#if SYMBIAN_DEBUG
print_node_brief (stderr, "", ctype, 0);
fprintf (stderr, " does NOT need to be EXPORTed [no key function]\n");
#endif
return false;
}
if (! TREE_ASM_WRITTEN (key))
{
#if SYMBIAN_DEBUG
print_node_brief (stderr, "", ctype, 0);
fprintf (stderr, " does NOT need to be EXPORTed [key function not defined]\n");
#endif
return false;
}
inline_ctor_dtor = false;
dllimport_ctor_dtor = false;
dllimport_member = false;
methods = CLASSTYPE_METHOD_VEC (ctype);
len = methods ? TREE_VEC_LENGTH (methods) : 0;
for (;len --;)
{
tree member = TREE_VEC_ELT (methods, len);
if (! member)
continue;
for (member = OVL_CURRENT (member); member; member = OVL_NEXT (member))
{
if (TREE_CODE (member) != FUNCTION_DECL)
continue;
if (DECL_CONSTRUCTOR_P (member) || DECL_DESTRUCTOR_P (member))
{
if (DECL_INLINE (member)
&& ( DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (member)
|| DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (member)))
inline_ctor_dtor = true;
if (lookup_attribute ("dllimport", DECL_ATTRIBUTES (member)))
dllimport_ctor_dtor = true;
}
else
{
if (DECL_PURE_VIRTUAL_P (member))
continue;
if (! DECL_VIRTUAL_P (member))
continue;
if (DECL_INLINE (member))
continue;
if (lookup_attribute ("dllimport", DECL_ATTRIBUTES (member)))
dllimport_member = true;
}
}
}
if (! dllimport_member && ! dllimport_ctor_dtor)
{
#if SYMBIAN_DEBUG
print_node_brief (stderr, "", ctype, 0);
fprintf (stderr,
" does NOT need to be EXPORTed [no non-pure virtuals or ctors/dtors with dllimport]\n");
#endif
return false;
}
if (! inline_ctor_dtor)
{
#if SYMBIAN_DEBUG
print_node_brief (stderr, "", ctype, 0);
fprintf (stderr,
" does NOT need to be EXPORTed [no inline ctor/dtor]\n");
#endif
return false;
}
#if SYMBIAN_DEBUG
print_node_brief (stderr, "", ctype, 0);
fprintf (stderr, " DOES need to be EXPORTed\n");
#endif
for (i = 0, binfo = TYPE_BINFO (ctype);
BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
symbian_possibly_export_base_class (BINFO_TYPE (base_binfo));
return true;
}
static void
symbian_add_attribute_to_class_vtable_and_rtti (tree ctype, const char *attr_name)
{
symbian_add_attribute (ctype, attr_name);
if (CLASSTYPE_VTABLES (ctype))
symbian_add_attribute (CLASSTYPE_VTABLES (ctype), attr_name);
if (TYPE_MAIN_VARIANT (ctype)
&& CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (ctype)))
symbian_add_attribute (CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (ctype)),
attr_name);
}
static bool
symbian_class_needs_attribute_p (tree ctype, const char *attribute_name)
{
if (TYPE_POLYMORPHIC_P (ctype)
&& CLASSTYPE_KEY_METHOD (ctype)
&& lookup_attribute (attribute_name,
DECL_ATTRIBUTES (CLASSTYPE_KEY_METHOD (ctype))))
return true;
if (TREE_CODE (ctype) == RECORD_TYPE)
{
tree methods = CLASSTYPE_METHOD_VEC (ctype);
unsigned int len = methods ? TREE_VEC_LENGTH (methods) : 0;
for (;len --;)
{
tree member = TREE_VEC_ELT (methods, len);
if (! member)
continue;
for (member = OVL_CURRENT (member);
member;
member = OVL_NEXT (member))
{
if (TREE_CODE (member) != FUNCTION_DECL)
continue;
if (DECL_PURE_VIRTUAL_P (member))
continue;
if (! DECL_VIRTUAL_P (member))
continue;
if (lookup_attribute (attribute_name, DECL_ATTRIBUTES (member)))
{
#if SYMBIAN_DEBUG
print_node_brief (stderr, "", ctype, 0);
fprintf (stderr, " inherits %s because", attribute_name);
print_node_brief (stderr, "", member, 0);
fprintf (stderr, " has it.\n");
#endif
return true;
}
}
}
}
#if SYMBIAN_DEBUG
print_node_brief (stderr, "", ctype, 0);
fprintf (stderr, " does not inherit %s\n", attribute_name);
#endif
return false;
}
int
symbian_import_export_class (tree ctype, int import_export)
{
const char *attr_name = NULL;
switch (import_export)
{
case 1: attr_name = "dllexport"; break;
case -1: attr_name = "dllimport"; break;
default: break;
}
if (attr_name
&& ! lookup_attribute (attr_name, TYPE_ATTRIBUTES (ctype)))
{
if (symbian_class_needs_attribute_p (ctype, attr_name))
symbian_add_attribute_to_class_vtable_and_rtti (ctype, attr_name);
if (symbian_export_vtable_and_rtti_p (ctype))
{
symbian_add_attribute_to_class_vtable_and_rtti (ctype, "dllexport");
import_export = 1;
if (CLASSTYPE_VTABLES (ctype))
DECL_EXTERNAL (CLASSTYPE_VTABLES (ctype)) = 1;
if (CLASSTYPE_KEY_METHOD (ctype))
{
tree class;
for (class = keyed_classes; class; class = TREE_CHAIN (class))
if (class == ctype)
break;
if (class == NULL_TREE)
{
#if SYMBIAN_DEBUG
print_node_brief (stderr, "Add node", ctype, 0);
fprintf (stderr, " to the keyed classes list\n");
#endif
keyed_classes = tree_cons (NULL_TREE, ctype, keyed_classes);
}
}
if (CLASS_TYPE_P (ctype))
TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (ctype)))) = 1;
}
}
return import_export;
}
tree cp_global_trees[CPTI_MAX] __attribute__((weak));
#if defined ENABLE_TREE_CHECKING && (GCC_VERSION >= 2007)
void lang_check_failed (const char *, int, const char *) __attribute__((weak));
void
lang_check_failed (const char *file, int line, const char *function)
{
internal_error ("lang_* check: failed in %s, at %s:%d",
function, trim_filename (file), line);
}
#endif