diff --git a/generator.py b/generator.py index 01a428e2..8b9ca54a 100755 --- a/generator.py +++ b/generator.py @@ -417,6 +417,23 @@ def get_whole_name(self, generator): def __str__(self): return self.canonical_type.whole_name if None != self.canonical_type else self.whole_name + def object_can_convert(self, generator, is_to_native = True): + if self.is_object: + keys = [] + if self.canonical_type != None: + keys.append(self.canonical_type.name) + keys.append(self.name) + if is_to_native: + to_native_dict = generator.config['conversions']['to_native'] + if NativeType.dict_has_key_re(to_native_dict, keys): + return True + else: + from_native_dict = generator.config['conversions']['from_native'] + if NativeType.dict_has_key_re(from_native_dict, keys): + return True + + return False + class NativeField(object): def __init__(self, cursor): cursor = cursor.canonical @@ -426,11 +443,34 @@ def __init__(self, cursor): self.location = cursor.location member_field_re = re.compile('m_(\w+)') match = member_field_re.match(self.name) + self.signature_name = self.name + self.ntype = NativeType.from_type(cursor.type) if match: self.pretty_name = match.group(1) else: self.pretty_name = self.name + @staticmethod + def can_parse(ntype): + if ntype.kind == cindex.TypeKind.POINTER: + return False + native_type = NativeType.from_type(ntype) + if ntype.kind == cindex.TypeKind.UNEXPOSED and native_type.name != "std::string": + return False + return True + + def generate_code(self, current_class = None, generator = None): + gen = current_class.generator if current_class else generator + config = gen.config + + if config['definitions'].has_key('public_field'): + tpl = Template(config['definitions']['public_field'], + searchList=[current_class, self]) + self.signature_name = str(tpl) + tpl = Template(file=os.path.join(gen.target, "templates", "public_field.c"), + searchList=[current_class, self]) + gen.impl_file.write(str(tpl)) + # return True if found default argument. def iterate_param_node(param_node, depth=1): for node in param_node.get_children(): @@ -658,6 +698,7 @@ def __init__(self, cursor, generator): self.namespaced_class_name = self.class_name self.parents = [] self.fields = [] + self.public_fields = [] self.methods = {} self.static_methods = {} self.generator = generator @@ -675,6 +716,7 @@ def __init__(self, cursor, generator): self.target_class_name = registration_name self.namespaced_class_name = get_namespaced_name(cursor) self.namespace_name = get_namespace_name(cursor) + self.record_deprecated_func = False self.parse() @property @@ -685,7 +727,9 @@ def parse(self): ''' parse the current cursor, getting all the necesary information ''' + #print "parse %s class begin" % (self.class_name) self._deep_iterate(self.cursor) + #print "parse %s class end" % (self.class_name) def methods_clean(self): ''' @@ -762,6 +806,8 @@ def generate_code(self): if self.generator.script_type == "lua": for m in self.override_methods_clean(): m['impl'].generate_code(self, is_override = True) + for m in self.public_fields: + m.generate_code(self) # generate register section register = Template(file=os.path.join(self.generator.target, "templates", "register.c"), searchList=[{"current_class": self}]) @@ -778,6 +824,8 @@ def generate_code(self): searchList=[{"current_class": self}]) self.doc_func_file.write(str(apidoc_fun_foot_script)) self.doc_func_file.close() + if self.record_deprecated_func: + self.record_deprecated_file.close() def _deep_iterate(self, cursor=None, depth=0): for node in cursor.get_children(): # print("%s%s - %s" % ("> " * depth, node.displayname, node.kind)) @@ -833,8 +881,19 @@ def _process_node(self, cursor): elif cursor.kind == cindex.CursorKind.FIELD_DECL: self.fields.append(NativeField(cursor)) + if self._current_visibility == cindex.AccessSpecifierKind.PUBLIC and NativeField.can_parse(cursor.type): + self.public_fields.append(NativeField(cursor)) elif cursor.kind == cindex.CursorKind.CXX_ACCESS_SPEC_DECL: self._current_visibility = cursor.get_access_specifier() + elif cursor.kind == cindex.CursorKind.CXX_METHOD and cursor.get_availability() == cindex.AvailabilityKind.DEPRECATED and self._current_visibility == cindex.AccessSpecifierKind.PUBLIC: + if self.generator.script_type == "lua" and self.generator.record_deprecated_func: + if not self.record_deprecated_func: + docdeprecatedfilepath = os.path.join(self.generator.outdir + "/deprecated", self.class_name + ".txt") + self.record_deprecated_file = open(docdeprecatedfilepath, "w+") + self.record_deprecated_file.write("Deprecated functions of " + self.class_name + "as follows:\n") + self.record_deprecated_func = True + m = NativeFunction(cursor) + self.record_deprecated_file.write(m.func_name + "\n") elif cursor.kind == cindex.CursorKind.CXX_METHOD and cursor.get_availability() != cindex.AvailabilityKind.DEPRECATED: # skip if variadic if self._current_visibility == cindex.AccessSpecifierKind.PUBLIC and not cursor.type.is_function_variadic(): @@ -926,6 +985,7 @@ def __init__(self, opts): self.script_control_cpp = opts['script_control_cpp'] == "yes" self.script_type = opts['script_type'] self.macro_judgement = opts['macro_judgement'] + self.record_deprecated_func = opts['record_deprecated_func'] == "yes" if opts['skip']: list_of_skips = re.split(",\n?", opts['skip']) @@ -1052,6 +1112,11 @@ def generate_code(self): if not os.path.exists(docfiledir): os.makedirs(docfiledir) + if self.script_type == "lua" and self.record_deprecated_func: + record_files_dir = self.outdir + "/deprecated" + if not os.path.exists(record_files_dir): + os.makedirs(record_files_dir) + if self.script_type == "lua": docfilepath = os.path.join(docfiledir, self.out_file + "_api.lua") else: @@ -1395,7 +1460,8 @@ def main(): 'out_file': opts.out_file or config.get(s, 'prefix'), 'script_control_cpp': config.get(s, 'script_control_cpp') if config.has_option(s, 'script_control_cpp') else 'no', 'script_type': t, - 'macro_judgement': config.get(s, 'macro_judgement') if config.has_option(s, 'macro_judgement') else None + 'macro_judgement': config.get(s, 'macro_judgement') if config.has_option(s, 'macro_judgement') else None, + 'record_deprecated_func' : config.get(s, 'record_deprecated_func') if config.has_option(s, 'record_deprecated_func') else 'no' } generator = Generator(gen_opts) generator.generate_code() diff --git a/targets/lua/conversions.yaml b/targets/lua/conversions.yaml index eb109800..417de813 100644 --- a/targets/lua/conversions.yaml +++ b/targets/lua/conversions.yaml @@ -4,6 +4,7 @@ definitions: ifunction: "lua_${generator.prefix}_${class_name}_${func_name}" sfunction: "lua_${generator.prefix}_${class_name}_${func_name}" constructor: "lua_${generator.prefix}_${class_name}_constructor" + public_field: "lua_${generator.prefix}_${class_name}" conversions: # some times you want to use a special native type when converting from spidermonkey to native # the most common case would be from JS-boolean to bool. Using "bool" will fail here since we diff --git a/targets/lua/templates/public_field.c b/targets/lua/templates/public_field.c new file mode 100644 index 00000000..ac5d5320 --- /dev/null +++ b/targets/lua/templates/public_field.c @@ -0,0 +1,100 @@ +## ===== member implementation template +int ${signature_name}_get${name}(lua_State* tolua_S) +{ + ${namespaced_class_name}* cobj = nullptr; +\#if COCOS2D_DEBUG >= 1 + tolua_Error tolua_err; + if (!tolua_isusertype(tolua_S,1,"${generator.scriptname_from_native($namespaced_class_name, $namespace_name)}",0,&tolua_err)) goto tolua_lerror; +\#endif + + cobj = (${namespaced_class_name}*)tolua_tousertype(tolua_S,1,0); + +\#if COCOS2D_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function '${signature_name}_get${name}'", nullptr); + return 0; + } +\#endif + + #if $ntype.is_object and not $ntype.object_can_convert($generator, False) + ${ntype.from_native({"generator": $generator, + "type_name": $ntype.namespaced_name.replace("*", ""), + "ntype": $ntype.get_whole_name($generator)+"*", + "level": 2, + "scriptname": $generator.scriptname_from_native($ntype.namespaced_name, $ntype.namespace_name), + "in_value":"&cobj->" + $pretty_name, + })}; + #else + ${ntype.from_native({"generator": $generator, + "type_name": $ntype.namespaced_name.replace("*", ""), + "ntype": $ntype.get_whole_name($generator), + "level": 2, + "scriptname": $generator.scriptname_from_native($ntype.namespaced_name, $ntype.namespace_name), + "in_value":"cobj->" + $pretty_name, + })}; + #end if + + return 1; +\#if COCOS2D_DEBUG >= 1 +tolua_lerror: + tolua_error(tolua_S,"#ferror in function '${signature_name}_get${name}'.",&tolua_err); + return 0; +\#endif +} + +int ${signature_name}_set${name}(lua_State* tolua_S) +{ + int argc = 0; + ${namespaced_class_name}* cobj = nullptr; + bool ok = true; + +\#if COCOS2D_DEBUG >= 1 + tolua_Error tolua_err; + if (!tolua_isusertype(tolua_S,1,"${generator.scriptname_from_native($namespaced_class_name, $namespace_name)}",0,&tolua_err)) goto tolua_lerror; +\#endif + + cobj = (${namespaced_class_name}*)tolua_tousertype(tolua_S,1,0); + +\#if COCOS2D_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function '${signature_name}_set${name}'", nullptr); + return 0; + } +\#endif + argc = lua_gettop(tolua_S) - 1; + + if (1 == argc) + { + #if $ntype.is_object and not $ntype.object_can_convert($generator) + ${ntype.to_string($generator)}* arg0 = nullptr; + #else + ${ntype.to_string($generator)} arg0; + #end if + ${ntype.to_native({"generator": $generator, + "arg_idx": 2, + "out_value": "arg0", + "lua_namespaced_class_name": $generator.scriptname_from_native($namespaced_class_name, $namespace_name), + "func_name": $name, + "scriptname": $generator.scriptname_from_native($ntype.namespaced_name, $ntype.namespace_name), + "level": 2, + "arg":$ntype, + })}; + #if $ntype.is_object and not $ntype.object_can_convert($generator) + cobj->$pretty_name = *arg0; + #else + cobj->$pretty_name = arg0; + #end if + return 0; + } + + CCLOG("%s has wrong number of arguments: %d, was expecting %d \n", "${generator.scriptname_from_native($namespaced_class_name, $namespace_name)}:${name}",argc, 1); + return 0; + +\#if COCOS2D_DEBUG >= 1 +tolua_lerror: + tolua_error(tolua_S,"#ferror in function '${signature_name}_get${name}'.",&tolua_err); + return 0; +\#endif +} diff --git a/targets/lua/templates/register.c b/targets/lua/templates/register.c index d95f7e1f..3617324b 100644 --- a/targets/lua/templates/register.c +++ b/targets/lua/templates/register.c @@ -1,17 +1,16 @@ -#set has_constructor = False #if $current_class.methods.has_key('constructor') -#set has_constructor = True ${current_class.methods.constructor.generate_code($current_class)} #end if # #set generator = $current_class.generator #set methods = $current_class.methods_clean() #set st_methods = $current_class.static_methods_clean() +#set public_fields = $current_class.public_fields # static int lua_${generator.prefix}_${current_class.class_name}_finalize(lua_State* tolua_S) { printf("luabindings: finalizing LUA object (${current_class.class_name})"); -#if $generator.script_control_cpp +#if not $current_class.is_ref_class and $current_class.has_constructor \#if COCOS2D_DEBUG >= 1 tolua_Error tolua_err; if ( @@ -26,7 +25,7 @@ static int lua_${generator.prefix}_${current_class.class_name}_finalize(lua_Stat \#if COCOS2D_DEBUG >= 1 if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", nullptr); \#endif - delete self; + CC_SAFE_DELETE(self); } return 0; \#if COCOS2D_DEBUG >= 1 @@ -42,13 +41,13 @@ int lua_register_${generator.prefix}_${current_class.class_name}(lua_State* tolu { tolua_usertype(tolua_S,"${generator.scriptname_from_native($current_class.namespaced_class_name, $current_class.namespace_name)}"); #if len($current_class.parents) > 0 - #if $generator.script_control_cpp and $current_class.has_constructor + #if not $current_class.is_ref_class and $current_class.has_constructor tolua_cclass(tolua_S,"${current_class.class_name}","${generator.scriptname_from_native($current_class.namespaced_class_name, $current_class.namespace_name)}","${generator.scriptname_from_native($current_class.parents[0].namespaced_class_name,$current_class.parents[0].namespace_name)}",lua_${generator.prefix}_${current_class.class_name}_finalize); #else tolua_cclass(tolua_S,"${current_class.class_name}","${generator.scriptname_from_native($current_class.namespaced_class_name, $current_class.namespace_name)}","${generator.scriptname_from_native($current_class.parents[0].namespaced_class_name,$current_class.parents[0].namespace_name)}",nullptr); #end if #else - #if $generator.script_control_cpp and $current_class.has_constructor + #if not $current_class.is_ref_class and $current_class.has_constructor tolua_cclass(tolua_S,"${current_class.class_name}","${generator.scriptname_from_native($current_class.namespaced_class_name, $current_class.namespace_name)}","",lua_${generator.prefix}_${current_class.class_name}_finalize); #else tolua_cclass(tolua_S,"${current_class.class_name}","${generator.scriptname_from_native($current_class.namespaced_class_name, $current_class.namespace_name)}","",nullptr); @@ -56,7 +55,7 @@ int lua_register_${generator.prefix}_${current_class.class_name}(lua_State* tolu #end if tolua_beginmodule(tolua_S,"${current_class.class_name}"); - #if has_constructor + #if $current_class.has_constructor tolua_function(tolua_S,"new",lua_${generator.prefix}_${current_class.class_name}_constructor); #end if #for m in methods @@ -67,6 +66,9 @@ int lua_register_${generator.prefix}_${current_class.class_name}(lua_State* tolu #set fn = m['impl'] tolua_function(tolua_S,"${m['name']}", ${fn.signature_name}); #end for + #for m in public_fields + tolua_variable(tolua_S,"${m.name}", ${m.signature_name}_get${m.name}, ${m.signature_name}_set${m.name}); + #end for tolua_endmodule(tolua_S); std::string typeName = typeid(${current_class.namespaced_class_name}).name(); g_luaType[typeName] = "${generator.scriptname_from_native($current_class.namespaced_class_name, $current_class.namespace_name)}";