mirror of https://github.com/swig/swig
534 lines
16 KiB
C++
534 lines
16 KiB
C++
/* -----------------------------------------------------------------------------
|
|
* This file is part of SWIG, which is licensed as a whole under version 3
|
|
* (or any later version) of the GNU General Public License. Some additional
|
|
* terms also apply to certain portions of SWIG. The full details of the SWIG
|
|
* license and copyrights can be found in the LICENSE and COPYRIGHT files
|
|
* included with the SWIG source code as distributed by the SWIG developers
|
|
* and at https://www.swig.org/legal.html.
|
|
*
|
|
* emit.cxx
|
|
*
|
|
* Useful functions for emitting various pieces of code.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
#include "swigmod.h"
|
|
#include "cparse.h"
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* emit_return_variable()
|
|
*
|
|
* Emits a variable declaration for a function return value.
|
|
* The variable name is always called result.
|
|
* n => Node of the method being wrapped
|
|
* rt => the return type
|
|
* f => the wrapper to generate code into
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
void emit_return_variable(Node *n, SwigType *rt, Wrapper *f) {
|
|
|
|
if (!GetFlag(n, "tmap:out:optimal")) {
|
|
if (rt && (SwigType_type(rt) != T_VOID)) {
|
|
SwigType *vt = cplus_value_type(rt);
|
|
SwigType *tt = vt ? vt : rt;
|
|
SwigType *lt = SwigType_ltype(tt);
|
|
String *lstr = SwigType_str(lt, Swig_cresult_name());
|
|
if (SwigType_ispointer(lt)) {
|
|
Wrapper_add_localv(f, Swig_cresult_name(), lstr, "= 0", NULL);
|
|
} else {
|
|
Wrapper_add_local(f, Swig_cresult_name(), lstr);
|
|
}
|
|
if (vt) {
|
|
Delete(vt);
|
|
}
|
|
Delete(lt);
|
|
Delete(lstr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* emit_parameter_variables()
|
|
*
|
|
* Emits a list of variable declarations for function parameters.
|
|
* The variable names are always called arg1, arg2, etc...
|
|
* l => the parameter list
|
|
* f => the wrapper to generate code into
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
void emit_parameter_variables(ParmList *l, Wrapper *f) {
|
|
|
|
Parm *p;
|
|
String *tm;
|
|
|
|
/* Emit function arguments */
|
|
Swig_cargs(f, l);
|
|
|
|
/* Attach typemaps to parameters */
|
|
Swig_typemap_attach_parms("default", l, f);
|
|
Swig_typemap_attach_parms("arginit", l, f);
|
|
|
|
/* Apply the arginit and default */
|
|
p = l;
|
|
while (p) {
|
|
tm = Getattr(p, "tmap:arginit");
|
|
if (tm) {
|
|
Printv(f->code, tm, "\n", NIL);
|
|
p = Getattr(p, "tmap:arginit:next");
|
|
} else {
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
|
|
/* Apply the default typemap */
|
|
p = l;
|
|
while (p) {
|
|
tm = Getattr(p, "tmap:default");
|
|
if (tm) {
|
|
Printv(f->code, tm, "\n", NIL);
|
|
p = Getattr(p, "tmap:default:next");
|
|
} else {
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* emit_attach_parmmaps()
|
|
*
|
|
* Attach the standard parameter related typemaps.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
void emit_attach_parmmaps(ParmList *l, Wrapper *f) {
|
|
Swig_typemap_attach_parms("in", l, f);
|
|
Swig_typemap_attach_parms("typecheck", l, 0);
|
|
Swig_typemap_attach_parms("argout", l, f);
|
|
Swig_typemap_attach_parms("check", l, f);
|
|
Swig_typemap_attach_parms("freearg", l, f);
|
|
|
|
{
|
|
/* Handle in typemaps with numinputs=0. */
|
|
Parm *p = l;
|
|
while (p) {
|
|
String *tm = Getattr(p, "tmap:in");
|
|
if (tm) {
|
|
if (checkAttribute(p, "tmap:in:numinputs", "0")) {
|
|
Printv(f->code, tm, "\n", NIL);
|
|
}
|
|
p = Getattr(p, "tmap:in:next");
|
|
} else {
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Perform a sanity check on "in" and "freearg" typemaps. These
|
|
must exactly match to avoid chaos. If a mismatch occurs, we
|
|
nuke the freearg typemap */
|
|
{
|
|
Parm *p = l;
|
|
Parm *npin, *npfreearg;
|
|
while (p) {
|
|
npin = Getattr(p, "tmap:in:next");
|
|
|
|
if (Getattr(p, "tmap:freearg")) {
|
|
npfreearg = Getattr(p, "tmap:freearg:next");
|
|
if (npin != npfreearg) {
|
|
while (p != npin) {
|
|
Delattr(p, "tmap:freearg");
|
|
Delattr(p, "tmap:freearg:next");
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
}
|
|
p = npin;
|
|
}
|
|
}
|
|
|
|
/* Check for variable length arguments with no input typemap.
|
|
If no input is defined, we set this to ignore and print a
|
|
message.
|
|
*/
|
|
{
|
|
Parm *p = l;
|
|
Parm *lp = 0;
|
|
while (p) {
|
|
if (!checkAttribute(p, "tmap:in:numinputs", "0")) {
|
|
lp = p;
|
|
p = Getattr(p, "tmap:in:next");
|
|
continue;
|
|
}
|
|
if (SwigType_isvarargs(Getattr(p, "type"))) {
|
|
Swig_warning(WARN_LANG_VARARGS, input_file, line_number, "Variable length arguments discarded.\n");
|
|
Setattr(p, "tmap:in", "");
|
|
}
|
|
lp = 0;
|
|
p = nextSibling(p);
|
|
}
|
|
|
|
/* Check if last input argument is variable length argument */
|
|
if (lp) {
|
|
p = lp;
|
|
while (p) {
|
|
if (SwigType_isvarargs(Getattr(p, "type"))) {
|
|
// Mark the head of the ParmList that it has varargs
|
|
Setattr(l, "emit:varargs", lp);
|
|
//Printf(stdout, "setting emit:varargs %s ... %s +++ %s\n", Getattr(l, "emit:varargs"), Getattr(l, "type"), Getattr(p, "type"));
|
|
break;
|
|
}
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* An equivalent type can be used in the typecheck typemap for SWIG to detect the overloading of equivalent
|
|
* target language types. This is primarily for the smartptr feature, where a pointer and a smart pointer
|
|
* are seen as equivalent types in the target language.
|
|
*/
|
|
{
|
|
Parm *p = l;
|
|
while (p) {
|
|
String *tm = Getattr(p, "tmap:typecheck");
|
|
if (tm) {
|
|
String *equivalent = Getattr(p, "tmap:typecheck:equivalent");
|
|
if (equivalent) {
|
|
String *precedence = Getattr(p, "tmap:typecheck:precedence");
|
|
if (precedence && Strcmp(precedence, "0") != 0)
|
|
Swig_error(Getfile(tm), Getline(tm), "The 'typecheck' typemap for %s contains an 'equivalent' attribute for a 'precedence' that is not set to SWIG_TYPECHECK_POINTER or 0.\n", SwigType_str(Getattr(p, "type"), 0));
|
|
SwigType *cpt = Swig_cparse_type(equivalent);
|
|
if (cpt) {
|
|
Setattr(p, "equivtype", cpt);
|
|
Delete(cpt);
|
|
} else {
|
|
Swig_error(Getfile(tm), Getline(tm), "Invalid type (%s) in 'equivalent' attribute in 'typecheck' typemap for type %s.\n", equivalent, SwigType_str(Getattr(p, "type"), 0));
|
|
}
|
|
}
|
|
p = Getattr(p, "tmap:typecheck:next");
|
|
} else {
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* emit_num_arguments()
|
|
*
|
|
* Calculate the total number of arguments. This function is safe for use
|
|
* with multi-argument typemaps which may change the number of arguments in
|
|
* strange ways.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
int emit_num_arguments(ParmList *parms) {
|
|
Parm *p = parms;
|
|
int nargs = 0;
|
|
|
|
while (p) {
|
|
if (Getattr(p, "tmap:in")) {
|
|
nargs += GetInt(p, "tmap:in:numinputs");
|
|
p = Getattr(p, "tmap:in:next");
|
|
} else {
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
|
|
/* DB 04/02/2003: Not sure this is necessary with tmap:in:numinputs */
|
|
/*
|
|
if (parms && (p = Getattr(parms,"emit:varargs"))) {
|
|
if (!nextSibling(p)) {
|
|
nargs--;
|
|
}
|
|
}
|
|
*/
|
|
return nargs;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* emit_num_required()
|
|
*
|
|
* Computes the number of required arguments. This function is safe for
|
|
* use with multi-argument typemaps and knows how to skip over everything
|
|
* properly. Note that parameters with default values are counted unless
|
|
* the compact default args option is on.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
int emit_num_required(ParmList *parms) {
|
|
Parm *p = parms;
|
|
int nargs = 0;
|
|
Parm *first_default_arg = 0;
|
|
int compactdefargs = ParmList_is_compactdefargs(p);
|
|
|
|
while (p) {
|
|
if (Getattr(p, "tmap:in") && checkAttribute(p, "tmap:in:numinputs", "0")) {
|
|
p = Getattr(p, "tmap:in:next");
|
|
} else {
|
|
if (Getattr(p, "tmap:default"))
|
|
break;
|
|
if (Getattr(p, "value")) {
|
|
if (!first_default_arg)
|
|
first_default_arg = p;
|
|
if (compactdefargs)
|
|
break;
|
|
}
|
|
nargs += GetInt(p, "tmap:in:numinputs");
|
|
if (Getattr(p, "tmap:in")) {
|
|
p = Getattr(p, "tmap:in:next");
|
|
} else {
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Print error message for non-default arguments following default arguments */
|
|
/* The error message is printed more than once with most language modules, this ought to be fixed */
|
|
if (first_default_arg) {
|
|
p = first_default_arg;
|
|
while (p) {
|
|
if (Getattr(p, "tmap:in") && checkAttribute(p, "tmap:in:numinputs", "0")) {
|
|
p = Getattr(p, "tmap:in:next");
|
|
} else {
|
|
if (!Getattr(p, "value") && (!Getattr(p, "tmap:default"))) {
|
|
Swig_error(Getfile(p), Getline(p), "Non-optional argument '%s' follows an optional argument.\n", Getattr(p, "name"));
|
|
}
|
|
if (Getattr(p, "tmap:in")) {
|
|
p = Getattr(p, "tmap:in:next");
|
|
} else {
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* DB 04/02/2003: Not sure this is necessary with tmap:in:numinputs */
|
|
/*
|
|
if (parms && (p = Getattr(parms,"emit:varargs"))) {
|
|
if (!nextSibling(p)) {
|
|
nargs--;
|
|
}
|
|
}
|
|
*/
|
|
return nargs;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* emit_isvarargs()
|
|
*
|
|
* Checks if a ParmList is a parameter list containing varargs.
|
|
* This function requires emit_attach_parmmaps to have been called beforehand.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
int emit_isvarargs(ParmList *p) {
|
|
if (!p)
|
|
return 0;
|
|
if (Getattr(p, "emit:varargs"))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* emit_isvarargs_function()
|
|
*
|
|
* Checks for varargs in a function/constructor (can be overloaded)
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
bool emit_isvarargs_function(Node *n) {
|
|
bool has_varargs = false;
|
|
Node *over = Getattr(n, "sym:overloaded");
|
|
if (over) {
|
|
for (Node *sibling = over; sibling; sibling = Getattr(sibling, "sym:nextSibling")) {
|
|
if (ParmList_has_varargs(Getattr(sibling, "parms"))) {
|
|
has_varargs = true;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
has_varargs = ParmList_has_varargs(Getattr(n, "parms")) ? true : false;
|
|
}
|
|
return has_varargs;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* void emit_mark_vararg_parms()
|
|
*
|
|
* Marks the vararg parameters which are to be ignored.
|
|
* Vararg parameters are marked as ignored if there is no 'in' varargs (...)
|
|
* typemap.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
void emit_mark_varargs(ParmList *l) {
|
|
Parm *p = l;
|
|
while (p) {
|
|
if (SwigType_isvarargs(Getattr(p, "type")))
|
|
if (!Getattr(p, "tmap:in"))
|
|
Setattr(p, "varargs:ignore", "1");
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
/* replace_contract_args. This function replaces argument names in contract
|
|
specifications. Used in conjunction with the %contract directive. */
|
|
|
|
static void replace_contract_args(Parm *cp, Parm *rp, String *s) {
|
|
while (cp && rp) {
|
|
String *n = Getattr(cp, "name");
|
|
if (n) {
|
|
Replace(s, n, Getattr(rp, "lname"), DOH_REPLACE_ID);
|
|
}
|
|
cp = nextSibling(cp);
|
|
rp = nextSibling(rp);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* int emit_action_code()
|
|
*
|
|
* Emits action code for a wrapper. Adds in exception handling code (%exception).
|
|
* eaction -> the action code to emit
|
|
* wrappercode -> the emitted code (output)
|
|
* ----------------------------------------------------------------------------- */
|
|
int emit_action_code(Node *n, String *wrappercode, String *eaction) {
|
|
assert(Getattr(n, "wrap:name"));
|
|
|
|
/* Look for except feature (%exception) */
|
|
String *tm = GetFlagAttr(n, "feature:except");
|
|
if (tm)
|
|
tm = Copy(tm);
|
|
if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) {
|
|
if (Strchr(tm, '$')) {
|
|
Swig_replace_special_variables(n, parentNode(n), tm);
|
|
Replaceall(tm, "$action", eaction);
|
|
}
|
|
Printv(wrappercode, tm, "\n", NIL);
|
|
Delete(tm);
|
|
return 1;
|
|
} else {
|
|
Printv(wrappercode, eaction, "\n", NIL);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* int emit_action()
|
|
*
|
|
* Emits the call to the wrapped function.
|
|
* Adds in exception specification exception handling and %exception code.
|
|
* ----------------------------------------------------------------------------- */
|
|
String *emit_action(Node *n) {
|
|
String *actioncode = NewStringEmpty();
|
|
String *tm;
|
|
String *action;
|
|
String *wrap;
|
|
ParmList *catchlist = Getattr(n, "catchlist");
|
|
|
|
/* Look for fragments */
|
|
{
|
|
String *fragment = Getattr(n, "feature:fragment");
|
|
if (fragment) {
|
|
char *c, *tok;
|
|
String *t = Copy(fragment);
|
|
c = Char(t);
|
|
tok = strtok(c, ",");
|
|
while (tok) {
|
|
String *fname = NewString(tok);
|
|
Setfile(fname, Getfile(n));
|
|
Setline(fname, Getline(n));
|
|
Swig_fragment_emit(fname);
|
|
Delete(fname);
|
|
tok = strtok(NULL, ",");
|
|
}
|
|
Delete(t);
|
|
}
|
|
}
|
|
|
|
/* Emit wrapper code (if any) */
|
|
wrap = Getattr(n, "wrap:code");
|
|
if (wrap && Swig_filebyname("header") != Getattr(n, "wrap:code:done")) {
|
|
File *f_code = Swig_filebyname("header");
|
|
if (f_code) {
|
|
Printv(f_code, wrap, NIL);
|
|
}
|
|
Setattr(n, "wrap:code:done", f_code);
|
|
}
|
|
|
|
action = Getattr(n, "feature:action");
|
|
if (!action)
|
|
action = Getattr(n, "wrap:action");
|
|
assert(action != 0);
|
|
|
|
/* Emit contract code (if any) */
|
|
if (Swig_contract_mode_get()) {
|
|
/* Preassertion */
|
|
tm = Getattr(n, "contract:preassert");
|
|
if (Len(tm)) {
|
|
Printv(actioncode, tm, "\n", NIL);
|
|
}
|
|
}
|
|
/* Exception handling code */
|
|
|
|
/* saves action -> eaction for postcatching exception */
|
|
String *eaction = NewString("");
|
|
|
|
/* If we are in C++ mode and there is an exception specification. We're going to
|
|
enclose the block in a try block */
|
|
if (catchlist) {
|
|
Printf(eaction, "try {\n");
|
|
}
|
|
|
|
String *preaction = Getattr(n, "wrap:preaction");
|
|
if (preaction)
|
|
Printv(eaction, preaction, NIL);
|
|
|
|
Printv(eaction, action, NIL);
|
|
|
|
String *postaction = Getattr(n, "wrap:postaction");
|
|
if (postaction)
|
|
Printv(eaction, postaction, NIL);
|
|
|
|
if (catchlist) {
|
|
int unknown_catch = 0;
|
|
int has_varargs = 0;
|
|
Printf(eaction, "}");
|
|
for (Parm *ep = catchlist; ep; ep = nextSibling(ep)) {
|
|
String *em = Swig_typemap_lookup("throws", ep, "_e", 0);
|
|
if (em) {
|
|
SwigType *et = Getattr(ep, "type");
|
|
SwigType *etr = SwigType_typedef_resolve_all(et);
|
|
if (SwigType_isreference(etr) || SwigType_ispointer(etr) || SwigType_isarray(etr)) {
|
|
Printf(eaction, " catch(%s) {", SwigType_str(et, "_e"));
|
|
} else if (SwigType_isvarargs(etr)) {
|
|
Printf(eaction, " catch(...) {");
|
|
has_varargs = 1;
|
|
} else {
|
|
Printf(eaction, " catch(%s) {", SwigType_str(et, "&_e"));
|
|
}
|
|
Printv(eaction, em, "\n", NIL);
|
|
Printf(eaction, "}");
|
|
} else {
|
|
Swig_warning(WARN_TYPEMAP_THROW, Getfile(n), Getline(n), "No 'throws' typemap defined for exception type '%s'\n", SwigType_str(Getattr(ep, "type"), 0));
|
|
unknown_catch = 1;
|
|
}
|
|
}
|
|
if (unknown_catch && !has_varargs) {
|
|
Printf(eaction, " catch(...) {\nthrow;\n}");
|
|
}
|
|
}
|
|
|
|
/* emit the except feature code */
|
|
emit_action_code(n, actioncode, eaction);
|
|
|
|
Delete(eaction);
|
|
|
|
/* Emit contract code (if any) */
|
|
if (Swig_contract_mode_get()) {
|
|
/* Postassertion */
|
|
tm = Getattr(n, "contract:postassert");
|
|
if (Len(tm)) {
|
|
Printv(actioncode, tm, "\n", NIL);
|
|
}
|
|
}
|
|
|
|
return actioncode;
|
|
}
|