diff options
Diffstat (limited to 'Source/Modules/lang.cxx')
-rw-r--r-- | Source/Modules/lang.cxx | 736 |
1 files changed, 313 insertions, 423 deletions
diff --git a/Source/Modules/lang.cxx b/Source/Modules/lang.cxx index f7979b611..92f0cebd3 100644 --- a/Source/Modules/lang.cxx +++ b/Source/Modules/lang.cxx @@ -4,7 +4,7 @@ * 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 http://www.swig.org/legal.html. + * and at https://www.swig.org/legal.html. * * lang.cxx * @@ -16,13 +16,18 @@ #include <ctype.h> /* default mode settings */ +static int directors_allowed = 0; +static int director_language = 0; static int director_mode = 0; static int director_protected_mode = 1; static int all_protected_mode = 0; static int naturalvar_mode = 0; Language *Language::this_ = 0; -/* Set director_protected_mode */ +int Swig_directors_enabled() { + return director_language && CPlusPlus && (directors_allowed || director_mode); +} + void Wrapper_director_mode_set(int flag) { director_mode = flag; } @@ -74,9 +79,11 @@ String *input_file = 0; int SmartPointer = 0; static Hash *classhash; -extern int GenerateDefault; extern int ForceExtern; extern int AddExtern; +extern "C" { + extern int UseWrapperSuffix; +} /* import modes */ @@ -219,104 +226,149 @@ int Dispatcher::emit_children(Node *n) { int Dispatcher::defaultHandler(Node *) { return SWIG_OK; } + int Dispatcher::extendDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::applyDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::clearDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::constantDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::fragmentDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::importDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::includeDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::insertDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::moduleDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::nativeDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::pragmaDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::typemapDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::typemapitemDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::typemapcopyDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::typesDirective(Node *n) { return defaultHandler(n); } + int Dispatcher::cDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::externDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::enumDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::enumvalueDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::enumforwardDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::classDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::templateDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::lambdaDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::classforwardDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::constructorDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::destructorDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::accessDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::usingDeclaration(Node *n) { return defaultHandler(n); } + int Dispatcher::namespaceDeclaration(Node *n) { return defaultHandler(n); } +Dispatcher::AccessMode Dispatcher::accessModeFromString(String *access) { + Dispatcher::AccessMode mode = PUBLIC; + if (Cmp(access, "public") == 0) { + mode = PUBLIC; + } else if (Cmp(access, "private") == 0) { + mode = PRIVATE; + } else if (Cmp(access, "protected") == 0) { + mode = PROTECTED; + } else { + assert(0); + } + return mode; +} + + /* Allocators */ Language::Language(): none_comparison(NewString("$arg != 0")), director_ctor_code(NewString("")), director_prot_ctor_code(0), +director_multiple_inheritance(1), +doxygenTranslator(NULL), symtabs(NewHash()), overloading(0), multiinput(0), -cplus_runtime(0), -directors(0) { +cplus_runtime(0) { symbolAddScope(""); // create top level/global symbol table scope argc_template_string = NewString("argc"); argv_template_string = NewString("argv[%d]"); @@ -324,17 +376,8 @@ directors(0) { /* Default director constructor code, passed to Swig_ConstructorToFunction */ Printv(director_ctor_code, "if ( $comparison ) { /* subclassed */\n", " $director_new \n", "} else {\n", " $nondirector_new \n", "}\n", NIL); - /* - Default director 'protected' constructor code, disabled by - default. Each language that needs it, has to define it. - */ - director_prot_ctor_code = 0; - director_multiple_inheritance = 1; - director_language = 0; assert(!this_); this_ = this; - - doxygenTranslator = NULL; } Language::~Language() { @@ -418,7 +461,7 @@ static Parm *nonvoid_parms(Parm *p) { * Returns the alternative value type needed in C++ for class value * types. When swig is not sure about using a plain $ltype value, * since the class doesn't have a default constructor, or it can't be - * assigned, you will get back 'SwigValueWrapper<type >'. + * assigned, you will get back 'SwigValueWrapper<(type)>'. * * ----------------------------------------------------------------------------- */ @@ -445,12 +488,7 @@ static Node *first_nontemplate(Node *n) { void swig_pragma(char *lang, char *name, char *value) { if (strcmp(lang, "swig") == 0) { - if ((strcmp(name, "make_default") == 0) || ((strcmp(name, "makedefault") == 0))) { - GenerateDefault = 1; - } else if ((strcmp(name, "no_default") == 0) || ((strcmp(name, "nodefault") == 0))) { - Swig_warning(WARN_DEPRECATED_NODEFAULT, "SWIG", 1, "dangerous, use %%nodefaultctor, %%nodefaultdtor instead.\n"); - GenerateDefault = 0; - } else if (strcmp(name, "attributefunction") == 0) { + if (strcmp(name, "attributefunction") == 0) { String *nvalue = NewString(value); char *s = strchr(Char(nvalue), ':'); if (!s) { @@ -722,23 +760,23 @@ int Language::typemapDirective(Node *n) { String *code = Getattr(n, "code"); Parm *kwargs = Getattr(n, "kwargs"); Node *items = firstChild(n); - static int namewarn = 0; + static int nameerror = 0; if (code && (Strstr(code, "$source") || (Strstr(code, "$target")))) { - Swig_warning(WARN_TYPEMAP_SOURCETARGET, Getfile(n), Getline(n), "Deprecated typemap feature ($source/$target).\n"); - if (!namewarn) { - Swig_warning(WARN_TYPEMAP_SOURCETARGET, Getfile(n), Getline(n), "The use of $source and $target in a typemap declaration is deprecated.\n\ + Swig_error(Getfile(n), Getline(n), "Obsolete typemap feature ($source/$target).\n"); + if (!nameerror) { + Swig_error(Getfile(n), Getline(n), "The use of $source and $target in a typemap declaration is no longer supported.\n\ For typemaps related to argument input (in,ignore,default,arginit,check), replace\n\ $source by $input and $target by $1. For typemaps related to return values (out,\n\ argout,ret,except), replace $source by $1 and $target by $result. See the file\n\ Doc/Manual/Typemaps.html for complete details.\n"); - namewarn = 1; + nameerror = 1; } } if (Strcmp(method, "except") == 0) { - Swig_warning(WARN_DEPRECATED_EXCEPT_TM, Getfile(n), Getline(n), "%%typemap(except) is deprecated. Use the %%exception directive.\n"); + Swig_error(Getfile(n), Getline(n), "%%typemap(except) is no longer supported. Use the %%exception directive.\n"); } if (Strcmp(method, "in") == 0) { @@ -765,16 +803,7 @@ Doc/Manual/Typemaps.html for complete details.\n"); } if (Strcmp(method, "ignore") == 0) { - Swig_warning(WARN_DEPRECATED_IGNORE_TM, Getfile(n), Getline(n), "%%typemap(ignore) has been replaced by %%typemap(in,numinputs=0).\n"); - - Clear(method); - Append(method, "in"); - Hash *k = NewHash(); - Setattr(k, "name", "numinputs"); - Setattr(k, "value", "0"); - set_nextSibling(k, kwargs); - Setattr(n, "kwargs", k); - kwargs = k; + Swig_error(Getfile(n), Getline(n), "%%typemap(ignore) is no longer supported. Use %%typemap(in,numinputs=0).\n"); } /* Replace $descriptor() macros */ @@ -879,11 +908,11 @@ int Language::cDeclaration(Node *n) { /* discards nodes following the access control rules */ if (cplus_mode != PUBLIC || !is_public(n)) { /* except for friends, they are not affected by access control */ - int isfriend = Cmp(storage, "friend") == 0; + int isfriend = (Strstr(storage, "friend") != NULL); if (!isfriend) { /* Check what the director needs. If the method is pure virtual, it is always needed. * Also wrap non-virtual protected members if asked for (allprotected mode). */ - if (!(directorsEnabled() && ((is_member_director(CurrentClass, n) && need_nonpublic_member(n)) || isNonVirtualProtectedAccess(n)))) { + if (!(Swig_directors_enabled() && ((is_member_director(CurrentClass, n) && need_nonpublic_member(n)) || isNonVirtualProtectedAccess(n)))) { return SWIG_NOWRAP; } // Prevent wrapping protected overloaded director methods more than once - @@ -1029,17 +1058,6 @@ int Language::cDeclaration(Node *n) { } } } - if (!SwigType_ismutable(ty)) { - SetFlag(n, "feature:immutable"); - } - /* If an array and elements are const, then read-only */ - if (SwigType_isarray(ty)) { - SwigType *tya = SwigType_array_type(ty); - if (SwigType_isconst(tya)) { - SetFlag(n, "feature:immutable"); - } - Delete(tya); - } DohIncref(type); Setattr(n, "type", ty); variableHandler(n); @@ -1058,7 +1076,7 @@ int Language::cDeclaration(Node *n) { int Language::functionHandler(Node *n) { String *storage = Getattr(n, "storage"); - int isfriend = CurrentClass && Cmp(storage, "friend") == 0; + int isfriend = CurrentClass && Strstr(storage, "friend"); int isstatic = CurrentClass && Swig_storage_isstatic(n) && !(SmartPointer && Getattr(n, "allocate:smartpointeraccess")); Parm *p = Getattr(n, "parms"); if (GetFlag(n, "feature:del")) { @@ -1084,7 +1102,7 @@ int Language::functionHandler(Node *n) { // This is a member function, set a flag so the documentation type is correct SetFlag(n, "memberfunction"); Node *explicit_n = 0; - if (directorsEnabled() && is_member_director(CurrentClass, n) && !extraDirectorProtectedCPPMethodsRequired()) { + if (Swig_directors_enabled() && is_member_director(CurrentClass, n) && !extraDirectorProtectedCPPMethodsRequired()) { bool virtual_but_not_pure_virtual = (!(Cmp(storage, "virtual")) && (Cmp(Getattr(n, "value"), "0") != 0)); if (virtual_but_not_pure_virtual) { // Add additional wrapper which makes an explicit call to the virtual method (ie not a virtual call) @@ -1148,7 +1166,20 @@ int Language::globalfunctionHandler(Node *n) { String *extendname = Getattr(n, "extendname"); String *call = Swig_cfunction_call(extendname ? extendname : name, parms); String *cres = Swig_cresult(type, Swig_cresult_name(), call); - Setattr(n, "wrap:action", cres); + String *friendusing = Getattr(n, "friendusing"); + if (friendusing) { + // Add a using directive to avoid having to possibly fully qualify the call to the friend function. + // Unconventional for SWIG generation, but the alternative is to implement Argument Dependent Lookup + // as friend functions are quirky and not visible, except for ADL. An ADL implementation would be needed + // in order to work out when the friend function is visible or not, in order to determine whether to + // rely on ADL (with no qualification) or to fully qualify the call to the friend function made + // visible via a matching declaration at namespace scope. + String *action = NewStringf("%s\n%s", friendusing, cres); + Setattr(n, "wrap:action", action); + Delete(action); + } else { + Setattr(n, "wrap:action", cres); + } Delete(cres); Delete(call); functionWrapper(n); @@ -1261,7 +1292,7 @@ int Language::memberfunctionHandler(Node *n) { } int DirectorExtraCall = 0; - if (directorsEnabled() && is_member_director(CurrentClass, n) && !SmartPointer) + if (Swig_directors_enabled() && is_member_director(CurrentClass, n) && !SmartPointer) if (extraDirectorProtectedCPPMethodsRequired()) DirectorExtraCall = CWRAP_DIRECTOR_TWO_CALLS; @@ -1307,29 +1338,36 @@ int Language::staticmemberfunctionHandler(Node *n) { else cname = NewStringf("%s::%s", sname, name); } else { - String *mname = Swig_name_mangle(ClassName); + String *classname_str = SwigType_namestr(ClassName); + String *mname = Swig_name_mangle_string(classname_str); cname = Swig_name_member(NSpace, mname, name); Delete(mname); + Delete(classname_str); } mrename = Swig_name_member(NSpace, ClassPrefix, symname); if (Extend) { String *code = Getattr(n, "code"); String *defaultargs = Getattr(n, "defaultargs"); - String *mangled = Swig_name_mangle(mrename); + String *mangled = Swig_name_mangle_string(mrename); Delete(mrename); mrename = mangled; - if (Getattr(n, "sym:overloaded") && code) { - Append(cname, Getattr(defaultargs ? defaultargs : n, "sym:overname")); - } + if (code) { + // See Swig_MethodToFunction() for the explanation of this code. + if (Getattr(n, "sym:overloaded")) { + Append(cname, Getattr(defaultargs ? defaultargs : n, "sym:overname")); + } else if (UseWrapperSuffix) { + Append(cname, "__SWIG"); + } - if (!defaultargs && code) { - /* Hmmm. An added static member. We have to create a little wrapper for this */ - String *mangled_cname = Swig_name_mangle(cname); - Swig_add_extension_code(n, mangled_cname, parms, type, code, CPlusPlus, 0); - Setattr(n, "extendname", mangled_cname); - Delete(mangled_cname); + if (!defaultargs) { + /* Hmmm. An added static member. We have to create a little wrapper for this */ + String *mangled_cname = Swig_name_mangle_string(cname); + Swig_add_extension_code(n, mangled_cname, parms, type, code, CPlusPlus, 0); + Setattr(n, "extendname", mangled_cname); + Delete(mangled_cname); + } } } @@ -1428,7 +1466,7 @@ int Language::membervariableHandler(Node *n) { /* Create a function to set the value of the variable */ - int assignable = is_assignable(n); + int assignable = !is_immutable(n); if (SmartPointer) { if (!Getattr(CurrentClass, "allocate:smartpointermutable")) { @@ -1481,8 +1519,6 @@ int Language::membervariableHandler(Node *n) { } else { String *pname0 = Swig_cparm_name(0, 0); String *pname1 = Swig_cparm_name(0, 1); - Replace(tm, "$source", pname1, DOH_REPLACE_ANY); - Replace(tm, "$target", target, DOH_REPLACE_ANY); Replace(tm, "$input", pname1, DOH_REPLACE_ANY); Replace(tm, "$self", pname0, DOH_REPLACE_ANY); Setattr(n, "wrap:action", tm); @@ -1793,6 +1829,8 @@ int Language::typedefHandler(Node *n) { */ SwigType *name = Getattr(n, "name"); SwigType *decl = Getattr(n, "decl"); + Setfile(name, Getfile(n)); + Setline(name, Getline(n)); if (!SwigType_ispointer(decl) && !SwigType_isreference(decl)) { SwigType *pname = Copy(name); SwigType_add_pointer(pname); @@ -1842,20 +1880,82 @@ static String *vtable_method_id(Node *n) { String *tmp = SwigType_pop_function(local_decl); Delete(local_decl); local_decl = tmp; - Node *method_id = NewStringf("%s|%s", name, local_decl); + String *method_id = NewStringf("%s|%s", name, local_decl); Delete(local_decl); return method_id; } +/* ---------------------------------------------------------------------- + * Language::unrollOneVirtualMethod() + * ---------------------------------------------------------------------- */ + +void Language::unrollOneVirtualMethod(String *classname, Node *n, Node *parent, List *vm, int &virtual_destructor, int protectedbase) { + if (!checkAttribute(n, "storage", "virtual")) + return; + if (GetFlag(n, "final")) + return; + + String *nodeType = Getattr(n, "nodeType"); + + /* we need to add methods(cdecl) and destructor (to check for throw decl) */ + int is_destructor = (Cmp(nodeType, "destructor") == 0); + if ((Cmp(nodeType, "cdecl") == 0) || is_destructor) { + String *decl = Getattr(n, "decl"); + /* extra check for function type and proper access */ + if (SwigType_isfunction(decl) && (((!protectedbase || dirprot_mode()) && is_public(n)) || need_nonpublic_member(n))) { + String *name = Getattr(n, "name"); + String *method_id = is_destructor ? NewStringf("~destructor") : vtable_method_id(n); + /* Make sure that the new method overwrites the existing: */ + int len = Len(vm); + const int DO_NOT_REPLACE = -1; + int replace = DO_NOT_REPLACE; + for (int i = 0; i < len; i++) { + Node *item = Getitem(vm, i); + String *check_vmid = Getattr(item, "vmid"); + + if (Strcmp(method_id, check_vmid) == 0) { + replace = i; + break; + } + } + /* filling a new method item */ + String *fqdname = NewStringf("%s::%s", classname, name); + Hash *item = NewHash(); + Setattr(item, "fqdname", fqdname); + Node *m = Copy(n); + + /* Store the complete return type - needed for non-simple return types (pointers, references etc.) */ + SwigType *ty = NewString(Getattr(m, "type")); + SwigType_push(ty, decl); + if (SwigType_isqualifier(ty)) { + Delete(SwigType_pop(ty)); + } + Delete(SwigType_pop_function(ty)); + Setattr(m, "returntype", ty); + + String *mname = NewStringf("%s::%s", Getattr(parent, "name"), name); + /* apply the features of the original method found in the base class */ + Swig_features_get(Swig_cparse_features(), 0, mname, Getattr(m, "decl"), m); + Setattr(item, "methodNode", m); + Setattr(item, "vmid", method_id); + if (replace == DO_NOT_REPLACE) + Append(vm, item); + else + Setitem(vm, replace, item); + Setattr(n, "directorNode", m); + + Delete(mname); + } + if (is_destructor) { + virtual_destructor = 1; + } + } +} /* ---------------------------------------------------------------------- * Language::unrollVirtualMethods() * ---------------------------------------------------------------------- */ -int Language::unrollVirtualMethods(Node *n, Node *parent, List *vm, int default_director, int &virtual_destructor, int protectedbase) { - Node *ni; - String *nodeType; - String *classname; - String *decl; +int Language::unrollVirtualMethods(Node *n, Node *parent, List *vm, int &virtual_destructor, int protectedbase) { bool first_base = false; // recurse through all base classes to build the vtable List *bl = Getattr(n, "bases"); @@ -1864,10 +1964,11 @@ int Language::unrollVirtualMethods(Node *n, Node *parent, List *vm, int default_ for (bi = First(bl); bi.item; bi = Next(bi)) { if (first_base && !director_multiple_inheritance) break; - unrollVirtualMethods(bi.item, parent, vm, default_director, virtual_destructor); + unrollVirtualMethods(bi.item, parent, vm, virtual_destructor); first_base = true; } } + // recurse through all protected base classes to build the vtable, as needed bl = Getattr(n, "protectedbases"); if (bl) { @@ -1875,88 +1976,28 @@ int Language::unrollVirtualMethods(Node *n, Node *parent, List *vm, int default_ for (bi = First(bl); bi.item; bi = Next(bi)) { if (first_base && !director_multiple_inheritance) break; - unrollVirtualMethods(bi.item, parent, vm, default_director, virtual_destructor, 1); + unrollVirtualMethods(bi.item, parent, vm, virtual_destructor, 1); first_base = true; } } + // find the methods that need directors - classname = Getattr(n, "name"); - for (ni = Getattr(n, "firstChild"); ni; ni = nextSibling(ni)) { + String *classname = Getattr(n, "name"); + for (Node *ni = firstChild(n); ni; ni = nextSibling(ni)) { /* we only need to check the virtual members */ - nodeType = Getattr(ni, "nodeType"); - int is_using = (Cmp(nodeType, "using") == 0); - Node *nn = is_using ? firstChild(ni) : ni; /* assume there is only one child node for "using" nodes */ - if (is_using) { - if (nn) - nodeType = Getattr(nn, "nodeType"); - else - continue; // A private "using" node - } - if (!checkAttribute(nn, "storage", "virtual")) - continue; - if (GetFlag(nn, "final")) - continue; - /* we need to add methods(cdecl) and destructor (to check for throw decl) */ - int is_destructor = (Cmp(nodeType, "destructor") == 0); - if ((Cmp(nodeType, "cdecl") == 0) || is_destructor) { - decl = Getattr(nn, "decl"); - /* extra check for function type and proper access */ - if (SwigType_isfunction(decl) && (((!protectedbase || dirprot_mode()) && is_public(nn)) || need_nonpublic_member(nn))) { - String *name = Getattr(nn, "name"); - Node *method_id = is_destructor ? NewStringf("~destructor") : vtable_method_id(nn); - /* Make sure that the new method overwrites the existing: */ - int len = Len(vm); - const int DO_NOT_REPLACE = -1; - int replace = DO_NOT_REPLACE; - for (int i = 0; i < len; i++) { - Node *item = Getitem(vm, i); - String *check_vmid = Getattr(item, "vmid"); - - if (Strcmp(method_id, check_vmid) == 0) { - replace = i; - break; - } - } - /* filling a new method item */ - String *fqdname = NewStringf("%s::%s", classname, name); - Hash *item = NewHash(); - Setattr(item, "fqdname", fqdname); - Node *m = Copy(nn); - - /* Store the complete return type - needed for non-simple return types (pointers, references etc.) */ - SwigType *ty = NewString(Getattr(m, "type")); - SwigType_push(ty, decl); - if (SwigType_isqualifier(ty)) { - Delete(SwigType_pop(ty)); - } - Delete(SwigType_pop_function(ty)); - Setattr(m, "returntype", ty); - - String *mname = NewStringf("%s::%s", Getattr(parent, "name"), name); - /* apply the features of the original method found in the base class */ - Swig_features_get(Swig_cparse_features(), 0, mname, Getattr(m, "decl"), m); - Setattr(item, "methodNode", m); - Setattr(item, "vmid", method_id); - if (replace == DO_NOT_REPLACE) - Append(vm, item); - else - Setitem(vm, replace, item); - Setattr(nn, "directorNode", m); - - Delete(mname); - } - if (is_destructor) { - virtual_destructor = 1; + if (Equal(nodeType(ni), "using")) { + for (Node *nn = firstChild(ni); nn; nn = Getattr(nn, "sym:nextSibling")) { + unrollOneVirtualMethod(classname, nn, parent, vm, virtual_destructor, protectedbase); } } + unrollOneVirtualMethod(classname, ni, parent, vm, virtual_destructor, protectedbase); } /* We delete all the nodirector methods. This prevents the generation of 'empty' director classes. - But this has to be done outside the previous 'for' - and the recursive loop!. + Done once we've collated all the virtual methods into vm. */ if (n == parent) { int len = Len(vm); @@ -2035,7 +2076,6 @@ int Language::classDirectorDisown(Node *n) { * ---------------------------------------------------------------------- */ int Language::classDirectorConstructors(Node *n) { - Node *ni; String *nodeType; Node *parent = Swig_methodclass(n); int default_ctor = Getattr(parent, "allocate:default_constructor") ? 1 : 0; @@ -2043,31 +2083,42 @@ int Language::classDirectorConstructors(Node *n) { int constructor = 0; /* emit constructors */ - for (ni = Getattr(n, "firstChild"); ni; ni = nextSibling(ni)) { + List *constructors = NewList(); + for (Node *ni = firstChild(n); ni; ni = nextSibling(ni)) { nodeType = Getattr(ni, "nodeType"); - if (Cmp(nodeType, "constructor") == 0) { - if (GetFlag(ni, "feature:ignore")) - continue; + if (Equal(nodeType, "constructor")) { + Append(constructors, ni); + } else if (Equal(nodeType, "using") && GetFlag(ni, "usingctor")) { + for (Node *ui = firstChild(ni); ui; ui = nextSibling(ui)) { + Append(constructors, ui); + } + } + } + for (Iterator it = First(constructors); it.item; it = Next(it)) { + Node *ni = it.item; + if (GetFlag(ni, "feature:ignore")) + continue; - Parm *parms = Getattr(ni, "parms"); - if (is_public(ni)) { - /* emit public constructor */ + Parm *parms = Getattr(ni, "parms"); + if (is_public(ni)) { + /* emit public constructor */ + classDirectorConstructor(ni); + constructor = 1; + if (default_ctor) + default_ctor = !ParmList_numrequired(parms); + } else { + /* emit protected constructor if needed */ + if (need_nonpublic_ctor(ni)) { classDirectorConstructor(ni); constructor = 1; + protected_ctor = 1; if (default_ctor) default_ctor = !ParmList_numrequired(parms); - } else { - /* emit protected constructor if needed */ - if (need_nonpublic_ctor(ni)) { - classDirectorConstructor(ni); - constructor = 1; - protected_ctor = 1; - if (default_ctor) - default_ctor = !ParmList_numrequired(parms); - } } } } + Delete(constructors); + /* emit default constructor if needed */ if (!constructor) { if (!default_ctor) { @@ -2193,13 +2244,13 @@ int Language::classDirector(Node *n) { } List *vtable = NewList(); int virtual_destructor = 0; - unrollVirtualMethods(n, n, vtable, 0, virtual_destructor); + unrollVirtualMethods(n, n, vtable, virtual_destructor); // Emit all the using base::member statements for non virtual members (allprotected mode) Node *ni; String *using_protected_members_code = NewString(""); for (ni = Getattr(n, "firstChild"); ni; ni = nextSibling(ni)) { - Node *nodeType = Getattr(ni, "nodeType"); + String *nodeType = nodeType(ni); if (Cmp(nodeType, "destructor") == 0 && GetFlag(ni, "final")) { String *classtype = Getattr(n, "classtype"); SWIG_WARN_NODE_BEGIN(ni); @@ -2210,13 +2261,18 @@ int Language::classDirector(Node *n) { Delete(using_protected_members_code); return SWIG_OK; } - bool cdeclaration = (Cmp(nodeType, "cdecl") == 0); - if (cdeclaration && !GetFlag(ni, "feature:ignore")) { - if (isNonVirtualProtectedAccess(ni)) { - Node *overloaded = Getattr(ni, "sym:overloaded"); + Node *nn = ni; + bool cdeclaration = Equal(nodeType, "cdecl"); + if (!cdeclaration && Equal(nodeType, "using")) { + nn = Getattr(ni, "firstChild"); + cdeclaration = nn && Equal(nodeType(nn), "cdecl") ? true : false; + } + if (cdeclaration && !GetFlag(nn, "feature:ignore")) { + if (isNonVirtualProtectedAccess(nn)) { + Node *overloaded = Getattr(nn, "sym:overloaded"); // emit the using base::member statement (but only once if the method is overloaded) - if (!overloaded || (overloaded && (overloaded == ni))) - Printf(using_protected_members_code, " using %s::%s;\n", SwigType_namestr(ClassName), Getattr(ni, "name")); + if (!overloaded || (overloaded && (overloaded == nn))) + Printf(using_protected_members_code, " using %s::%s;\n", SwigType_namestr(ClassName), Getattr(nn, "name")); } } } @@ -2249,164 +2305,6 @@ int Language::classDirector(Node *n) { * Language::classDeclaration() * ---------------------------------------------------------------------- */ -static void addCopyConstructor(Node *n) { - Node *cn = NewHash(); - set_nodeType(cn, "constructor"); - Setattr(cn, "access", "public"); - Setfile(cn, Getfile(n)); - Setline(cn, Getline(n)); - - String *cname = Getattr(n, "name"); - SwigType *type = Copy(cname); - String *name = Swig_scopename_last(cname); - String *cc = NewStringf("r.q(const).%s", type); - String *decl = NewStringf("f(%s).", cc); - String *oldname = Getattr(n, "sym:name"); - - if (Getattr(n, "allocate:has_constructor")) { - // to work properly with '%rename Class', we must look - // for any other constructor in the class, which has not been - // renamed, and use its name as oldname. - Node *c; - for (c = firstChild(n); c; c = nextSibling(c)) { - const char *tag = Char(nodeType(c)); - if (strcmp(tag, "constructor") == 0) { - String *cname = Getattr(c, "name"); - String *csname = Getattr(c, "sym:name"); - String *clast = Swig_scopename_last(cname); - if (Equal(csname, clast)) { - oldname = csname; - break; - } - } - } - } - - String *symname = Swig_name_make(cn, cname, name, decl, oldname); - if (Strcmp(symname, "$ignore") != 0) { - Parm *p = NewParm(cc, "other", n); - - Setattr(cn, "name", name); - Setattr(cn, "sym:name", symname); - SetFlag(cn, "feature:new"); - Setattr(cn, "decl", decl); - Setattr(cn, "parentNode", n); - Setattr(cn, "parms", p); - Setattr(cn, "copy_constructor", "1"); - - Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab")); - Node *on = Swig_symbol_add(symname, cn); - Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn); - Swig_symbol_setscope(oldscope); - - if (on == cn) { - Node *access = NewHash(); - set_nodeType(access, "access"); - Setattr(access, "kind", "public"); - appendChild(n, access); - appendChild(n, cn); - Setattr(n, "has_copy_constructor", "1"); - Setattr(n, "copy_constructor_decl", decl); - Setattr(n, "allocate:copy_constructor", "1"); - Delete(access); - } - } - Delete(cn); - Delete(name); - Delete(decl); - Delete(symname); -} - -static void addDefaultConstructor(Node *n) { - Node *cn = NewHash(); - set_nodeType(cn, "constructor"); - Setattr(cn, "access", "public"); - Setfile(cn, Getfile(n)); - Setline(cn, Getline(n)); - - String *cname = Getattr(n, "name"); - String *name = Swig_scopename_last(cname); - String *decl = NewString("f()."); - String *oldname = Getattr(n, "sym:name"); - String *symname = Swig_name_make(cn, cname, name, decl, oldname); - if (Strcmp(symname, "$ignore") != 0) { - Setattr(cn, "name", name); - Setattr(cn, "sym:name", symname); - SetFlag(cn, "feature:new"); - Setattr(cn, "decl", decl); - Setattr(cn, "parentNode", n); - Setattr(cn, "default_constructor", "1"); - Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab")); - Node *on = Swig_symbol_add(symname, cn); - Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn); - Swig_symbol_setscope(oldscope); - - if (on == cn) { - Node *access = NewHash(); - set_nodeType(access, "access"); - Setattr(access, "kind", "public"); - appendChild(n, access); - appendChild(n, cn); - Setattr(n, "has_default_constructor", "1"); - Setattr(n, "allocate:default_constructor", "1"); - Delete(access); - } - } - Delete(cn); - Delete(name); - Delete(decl); - Delete(symname); -} - -static void addDestructor(Node *n) { - Node *cn = NewHash(); - set_nodeType(cn, "destructor"); - Setattr(cn, "access", "public"); - Setfile(cn, Getfile(n)); - Setline(cn, Getline(n)); - - String *cname = Getattr(n, "name"); - String *name = Swig_scopename_last(cname); - Insert(name, 0, "~"); - String *decl = NewString("f()."); - String *symname = Swig_name_make(cn, cname, name, decl, 0); - if (Strcmp(symname, "$ignore") != 0) { - String *possible_nonstandard_symname = NewStringf("~%s", Getattr(n, "sym:name")); - - Setattr(cn, "name", name); - Setattr(cn, "sym:name", symname); - Setattr(cn, "decl", "f()."); - Setattr(cn, "parentNode", n); - - Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab")); - Node *nonstandard_destructor = Equal(possible_nonstandard_symname, symname) ? 0 : Swig_symbol_clookup(possible_nonstandard_symname, 0); - Node *on = Swig_symbol_add(symname, cn); - Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn); - Swig_symbol_setscope(oldscope); - - if (on == cn) { - // SWIG accepts a non-standard named destructor in %extend that uses a typedef for the destructor name - // For example: typedef struct X {} XX; %extend X { ~XX() {...} } - // Don't add another destructor if a nonstandard one has been declared - if (!nonstandard_destructor) { - Node *access = NewHash(); - set_nodeType(access, "access"); - Setattr(access, "kind", "public"); - appendChild(n, access); - appendChild(n, cn); - Setattr(n, "has_destructor", "1"); - Setattr(n, "allocate:destructor", "1"); - Delete(access); - } - } - Delete(possible_nonstandard_symname); - } - Delete(cn); - Delete(name); - Delete(decl); - Delete(symname); -} - int Language::classDeclaration(Node *n) { String *ochildren = Getattr(n, "feature:onlychildren"); if (ochildren) { @@ -2479,44 +2377,19 @@ int Language::classDeclaration(Node *n) { /* Call classHandler() here */ if (!ImportMode) { - if (directorsEnabled()) { + if (Swig_directors_enabled()) { int ndir = GetFlag(n, "feature:director"); int nndir = GetFlag(n, "feature:nodirector"); /* 'nodirector' has precedence over 'director' */ dir = (ndir || nndir) ? (ndir && !nndir) : 0; } - int abstract = !dir && abstractClassTest(n); - int odefault = (GenerateDefault && !GetFlag(n, "feature:nodefault")); - - /* default constructor */ - if (!abstract && !GetFlag(n, "feature:nodefaultctor") && odefault) { - if (!Getattr(n, "has_constructor") && !Getattr(n, "allocate:has_constructor") && (Getattr(n, "allocate:default_constructor"))) { - addDefaultConstructor(n); - } - } - /* copy constructor */ - if (CPlusPlus && !abstract && GetFlag(n, "feature:copyctor")) { - if (!Getattr(n, "has_copy_constructor") && !Getattr(n, "allocate:has_copy_constructor") - && (Getattr(n, "allocate:copy_constructor")) - && (!GetFlag(n, "feature:ignore"))) { - addCopyConstructor(n); - } - } - /* default destructor */ - if (!GetFlag(n, "feature:nodefaultdtor") && odefault) { - if (!Getattr(n, "has_destructor") && (!Getattr(n, "allocate:has_destructor")) - && (Getattr(n, "allocate:default_destructor")) - && (!GetFlag(n, "feature:ignore"))) { - addDestructor(n); - } - } if (dir) { DirectorClassName = directorClassName(n); classDirector(n); } - /* check for abstract after resolving directors */ + /* check for abstract after resolving directors */ Abstract = abstractClassTest(n); classHandler(n); } else { @@ -2674,19 +2547,24 @@ int Language::constructorDeclaration(Node *n) { Setattr(CurrentClass, "sym:cleanconstructor", "1"); } - if ((cplus_mode != PUBLIC)) { + if (!is_public(n)) { /* check only for director classes */ if (!Swig_directorclass(CurrentClass) || !need_nonpublic_ctor(n)) return SWIG_NOWRAP; } - /* Name adjustment for %name */ + // Name adjustment of constructor when a class has been renamed with %rename Swig_save("constructorDeclaration", n, "sym:name", NIL); { String *base = Swig_scopename_last(name); - if ((Strcmp(base, symname) == 0) && (Strcmp(symname, ClassPrefix) != 0)) { - Setattr(n, "sym:name", ClassPrefix); + // Note that it is possible for the constructor to have a different name to the class name in + // some target languages, where it is wrapped as a factory type function instead of a constructor. + if (Equal(base, symname) && !Equal(symname, ClassPrefix)) { + // Adjust name, except when the constructor's name comes from a templated constructor, + // where the name passed to %template is used instead. + if (!Getattr(n, "template")) + Setattr(n, "sym:name", ClassPrefix); } Delete(base); } @@ -2752,7 +2630,6 @@ int Language::constructorDeclaration(Node *n) { constructorHandler(n); } } - Setattr(CurrentClass, "has_constructor", "1"); Swig_restore(n); return SWIG_OK; @@ -2869,16 +2746,29 @@ int Language::destructorDeclaration(Node *n) { Setattr(n, "sym:name", ClassPrefix); } - String *expected_name = ClassName; - String *scope = Swig_scopename_check(ClassName) ? Swig_scopename_prefix(ClassName) : 0; - String *actual_name = scope ? NewStringf("%s::%s", scope, name) : NewString(name); - Delete(scope); - Replace(actual_name, "~", "", DOH_REPLACE_FIRST); + String *nprefix = 0; + String *nlast = 0; + String *tprefix; + Swig_scopename_split(ClassName, &nprefix, &nlast); + tprefix = SwigType_templateprefix(nlast); + String *expected_name = NewStringf("~%s", tprefix); + + String *actual_name = Copy(name); if (!Equal(actual_name, expected_name) && !(Getattr(n, "template"))) { bool illegal_name = true; if (Extend) { // Check for typedef names used as a destructor name in %extend. This is deprecated except for anonymous // typedef structs which have had their symbol names adjusted to the typedef name in the parser. + Replace(actual_name, "~", "", DOH_REPLACE_FIRST); + Replace(expected_name, "~", "", DOH_REPLACE_FIRST); + if (Len(nprefix) > 0) { + String *old_actual_name = actual_name; + String *old_expected_name = expected_name; + actual_name = NewStringf("%s::%s", nprefix, actual_name); + expected_name = NewStringf("%s::%s", nprefix, expected_name); + Delete(old_expected_name); + Delete(old_actual_name); + } SwigType *name_resolved = SwigType_typedef_resolve_all(actual_name); SwigType *expected_name_resolved = SwigType_typedef_resolve_all(expected_name); @@ -2900,6 +2790,11 @@ int Language::destructorDeclaration(Node *n) { if (illegal_name) { Swig_warning(WARN_LANG_ILLEGAL_DESTRUCTOR, input_file, line_number, "Illegal destructor name %s. Ignored.\n", Swig_name_decl(n)); Swig_restore(n); + Delete(tprefix); + Delete(nlast); + Delete(nprefix); + Delete(expected_name); + Delete(actual_name); return SWIG_NOWRAP; } } @@ -2907,6 +2802,11 @@ int Language::destructorDeclaration(Node *n) { Setattr(CurrentClass, "has_destructor", "1"); Swig_restore(n); + Delete(tprefix); + Delete(nlast); + Delete(nprefix); + Delete(expected_name); + Delete(actual_name); return SWIG_OK; } @@ -2939,14 +2839,7 @@ int Language::destructorHandler(Node *n) { * ---------------------------------------------------------------------- */ int Language::accessDeclaration(Node *n) { - String *kind = Getattr(n, "kind"); - if (Cmp(kind, "public") == 0) { - cplus_mode = PUBLIC; - } else if (Cmp(kind, "private") == 0) { - cplus_mode = PRIVATE; - } else if (Cmp(kind, "protected") == 0) { - cplus_mode = PROTECTED; - } + cplus_mode = accessModeFromString(Getattr(n, "kind")); return SWIG_OK; } @@ -3028,7 +2921,7 @@ int Language::variableWrapper(Node *n) { } /* If no way to set variables. We simply create functions */ - int assignable = is_assignable(n); + int assignable = !is_immutable(n); int flags = use_naturalvar_mode(n); if (!GetFlag(n, "wrappedasconstant")) flags = flags | Extend; @@ -3049,8 +2942,6 @@ int Language::variableWrapper(Node *n) { } } else { String *pname0 = Swig_cparm_name(0, 0); - Replace(tm, "$source", pname0, DOH_REPLACE_ANY); - Replace(tm, "$target", name, DOH_REPLACE_ANY); Replace(tm, "$input", pname0, DOH_REPLACE_ANY); Setattr(n, "wrap:action", tm); Delete(tm); @@ -3127,8 +3018,8 @@ void Language::main(int argc, char *argv[]) { * ----------------------------------------------------------------------------- */ int Language::addSymbol(const String *s, const Node *n, const_String_or_char_ptr scope) { - //Printf( stdout, "addSymbol: %s %s\n", s, scope ); - Hash *symbols = Getattr(symtabs, scope ? scope : ""); + //Printf( stdout, "addSymbol: %s %s %s:%d\n", s, scope, Getfile(n), Getline(n) ); + Hash *symbols = symbolScopeLookup(scope); if (!symbols) { symbols = symbolAddScope(scope); } else { @@ -3175,15 +3066,15 @@ int Language::addInterfaceSymbol(const String *interface_name, Node *n, const_St * Language::symbolAddScope() * * Creates a scope (symbols Hash) for given name. This method is auxiliary, - * you don't have to call it - addSymbols will lazily create scopes automatically. + * you don't have to call it - addSymbol will lazily create scopes automatically. * If scope with given name already exists, then do nothing. * Returns newly created (or already existing) scope. * ----------------------------------------------------------------------------- */ -Hash* Language::symbolAddScope(const_String_or_char_ptr scope) { +Hash *Language::symbolAddScope(const_String_or_char_ptr scope/*, Node *n*/) { Hash *symbols = symbolScopeLookup(scope); - if(!symbols) { + if (!symbols) { // The order in which the following code is executed is important. In the Language - // constructor addScope("") is called to create a top level scope. + // constructor symbolAddScope("") is called to create a top level scope. // Thus we must first add a symbols hash to symtab and only then add pseudo // symbols to the top-level scope. @@ -3195,8 +3086,22 @@ Hash* Language::symbolAddScope(const_String_or_char_ptr scope) { // Alternatively the target language must add it in before attempting to add symbols into the scope. const_String_or_char_ptr top_scope = ""; Hash *topscope_symbols = Getattr(symtabs, top_scope); - Hash *pseudo_symbol = NewHash(); - Setattr(pseudo_symbol, "sym:scope", "1"); + + // TODO: + // Stop using pseudo scopes, the symbol Node containing the new scope should be passed into this function. + // This will require explicit calls to symbolScopeLookup() in each language and removing the call from addSymbol(). + // addSymbol() should then instead assert that the scope exists. + // All this just to fix up the file/line numbering of the scopes for error reporting. + //Node *symbol = n; + Node *symbol = Getattr(topscope_symbols, scope); + + Hash *pseudo_symbol = 0; + if (symbol) { + pseudo_symbol = symbol; + } else { + pseudo_symbol = NewHash(); + Setattr(pseudo_symbol, "sym:scope", "1"); + } Setattr(topscope_symbols, scope, pseudo_symbol); } return symbols; @@ -3208,7 +3113,7 @@ Hash* Language::symbolAddScope(const_String_or_char_ptr scope) { * Lookup and returns a symtable (hash) representing given scope. Hash contains * all symbols in this scope. * ----------------------------------------------------------------------------- */ -Hash* Language::symbolScopeLookup( const_String_or_char_ptr scope ) { +Hash *Language::symbolScopeLookup(const_String_or_char_ptr scope) { Hash *symbols = Getattr(symtabs, scope ? scope : ""); return symbols; } @@ -3227,7 +3132,7 @@ Hash* Language::symbolScopeLookup( const_String_or_char_ptr scope ) { * There is no difference from symbolLookup() method except for signature * and return type. * ----------------------------------------------------------------------------- */ -Hash* Language::symbolScopePseudoSymbolLookup( const_String_or_char_ptr scope ) +Hash *Language::symbolScopePseudoSymbolLookup(const_String_or_char_ptr scope) { /* Getting top scope */ const_String_or_char_ptr top_scope = ""; @@ -3254,6 +3159,7 @@ void Language::dumpSymbols() { while (it.key) { String *symname = it.key; Printf(stdout, " %s\n", symname); + //Printf(stdout, " %s (%s:%d)\n", symname, Getfile(it.item), Getline(it.item)); it = Next(it); } } @@ -3477,19 +3383,19 @@ int Language::cplus_runtime_mode() { } /* ----------------------------------------------------------------------------- - * Language::allow_directors() + * Language::directorLanguage() * ----------------------------------------------------------------------------- */ -void Language::allow_directors(int val) { - directors = val; +void Language::directorLanguage(int val) { + director_language = val; } /* ----------------------------------------------------------------------------- - * Language::directorsEnabled() + * Language::allow_directors() * ----------------------------------------------------------------------------- */ -int Language::directorsEnabled() const { - return director_language && CPlusPlus && (directors || director_mode); +void Language::allow_directors(int val) { + directors_allowed = val; } /* ----------------------------------------------------------------------------- @@ -3513,7 +3419,7 @@ void Language::allow_allprotected(int val) { * ----------------------------------------------------------------------------- */ int Language::dirprot_mode() const { - return directorsEnabled() ? director_protected_mode : 0; + return Swig_directors_enabled() ? director_protected_mode : 0; } /* ----------------------------------------------------------------------------- @@ -3549,7 +3455,7 @@ int Language::need_nonpublic_ctor(Node *n) { members, and use %ignore for the method you don't want to add in the director class. */ - if (directorsEnabled()) { + if (Swig_directors_enabled()) { if (is_protected(n)) { if (dirprot_mode()) { /* when using dirprot mode, the protected constructors are @@ -3582,7 +3488,7 @@ int Language::need_nonpublic_ctor(Node *n) { * Language::need_nonpublic_member() * ----------------------------------------------------------------------------- */ int Language::need_nonpublic_member(Node *n) { - if (directorsEnabled() && DirectorClassName) { + if (Swig_directors_enabled() && DirectorClassName) { if (is_protected(n)) { if (dirprot_mode()) { /* when using dirprot mode, the protected members are always needed. */ @@ -3624,15 +3530,20 @@ String *Language::makeParameterName(Node *n, Parm *p, int arg_num, bool setter) // Check if parameter name is a duplicate. int count = 0; + Parm *first_duplicate_parm = 0; ParmList *plist = Getattr(n, "parms"); while (plist) { - if ((Cmp(pn, Getattr(plist, "name")) == 0)) + if ((Cmp(pn, Getattr(plist, "name")) == 0)) { + if (!first_duplicate_parm) + first_duplicate_parm = plist; count++; + } plist = nextSibling(plist); } // If the parameter has no name at all or has a non-unique name, replace it with "argN". - if (!pn || count > 1) { + // On the assumption that p is pointer/element in plist, only replace the 2nd and subsequent duplicates + if (!pn || (count > 1 && p != first_duplicate_parm)) { arg = NewStringf("arg%d", arg_num); } else { // Otherwise, try to use the original C name, but modify it if necessary to avoid conflicting with the language keywords. @@ -3742,10 +3653,10 @@ String *Language::getClassType() const { } /* ----------------------------------------------------------------------------- - * Language::abstractClassTest() + * Dispatcher::abstractClassTest() * ----------------------------------------------------------------------------- */ //#define SWIG_DEBUG -int Language::abstractClassTest(Node *n) { +int Dispatcher::abstractClassTest(Node *n) { /* check for non public operator new */ if (GetFlag(n, "feature:notabstract")) return 0; @@ -3774,7 +3685,7 @@ int Language::abstractClassTest(Node *n) { #endif if (!labs) return 0; /*strange, but need to be fixed */ - if (abstracts && !directorsEnabled()) + if (abstracts && !Swig_directors_enabled()) return 1; if (!GetFlag(n, "feature:director")) return 1; @@ -3787,7 +3698,7 @@ int Language::abstractClassTest(Node *n) { #endif for (int i = 0; i < labs; i++) { Node *ni = Getitem(abstracts, i); - Node *method_id = vtable_method_id(ni); + String *method_id = vtable_method_id(ni); if (!method_id) continue; bool exists_item = false; @@ -3837,29 +3748,8 @@ void Language::setOverloadResolutionTemplates(String *argc, String *argv) { argv_template_string = Copy(argv); } -int Language::is_assignable(Node *n) { - if (GetFlag(n, "feature:immutable")) - return 0; - SwigType *type = Getattr(n, "type"); - Node *cn = 0; - SwigType *ftd = SwigType_typedef_resolve_all(type); - SwigType *td = SwigType_strip_qualifiers(ftd); - if (SwigType_type(td) == T_USER) { - cn = Swig_symbol_clookup(td, 0); - if (cn) { - if ((Strcmp(nodeType(cn), "class") == 0)) { - if (Getattr(cn, "allocate:noassign")) { - SetFlag(n, "feature:immutable"); - Delete(ftd); - Delete(td); - return 0; - } - } - } - } - Delete(ftd); - Delete(td); - return 1; +int Language::is_immutable(Node *n) { + return GetFlag(n, "feature:immutable"); } String *Language::runtimeCode() { |