diff --git a/generator.py b/generator.py index 00bdc9ea..0958d0da 100755 --- a/generator.py +++ b/generator.py @@ -414,6 +414,23 @@ def get_whole_name(self, generator): return 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 + def __str__(self): return self.canonical_type.whole_name if None != self.canonical_type else self.whole_name @@ -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(): @@ -674,6 +714,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 @@ -778,6 +819,9 @@ 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: + if self.generator.should_bind_field(self.class_name, m.name): + m.generate_code(self) # generate register section register = Template(file=os.path.join(self.generator.target, "templates", "register.c"), searchList=[{"current_class": self}]) @@ -850,6 +894,8 @@ 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: @@ -936,6 +982,7 @@ def __init__(self, opts): self.impl_file = None self.head_file = None self.skip_classes = {} + self.bind_fields = {} self.generated_classes = {} self.rename_functions = {} self.rename_classes = {} @@ -973,6 +1020,16 @@ def __init__(self, opts): self.skip_classes[class_name] = match.group(1).split(" ") else: raise Exception("invalid list of skip methods") + if opts['field']: + list_of_fields = re.split(",\n?", opts['field']) + for field in list_of_fields: + class_name, fields = field.split("::") + self.bind_fields[class_name] = [] + match = re.match("\[([^]]+)\]", fields) + if match: + self.bind_fields[class_name] = match.group(1).split(" ") + else: + raise Exception("invalid list of bind fields") if opts['rename_functions']: list_of_function_renames = re.split(",\n?", opts['rename_functions']) for rename in list_of_function_renames: @@ -1031,6 +1088,28 @@ def should_skip(self, class_name, method_name, verbose=False): print "%s will be accepted (%s, %s)" % (class_name, key, self.skip_classes[key]) return False + def should_bind_field(self, class_name, field_name, verbose=False): + if class_name == "*" and self.bind_fields.has_key("*"): + for func in self.bind_fields["*"]: + if re.match(func, method_name): + return True + else: + for key in self.bind_fields.iterkeys(): + if key == "*" or re.match("^" + key + "$", class_name): + if verbose: + print "%s in bind_fields" % (class_name) + if len(self.bind_fields[key]) == 1 and self.bind_fields[key][0] == "*": + if verbose: + print "All public fields of %s will be bound" % (class_name) + return True + if field_name != None: + for field in self.bind_fields[key]: + if re.match(field, field_name): + if verbose: + print "Field %s of %s will be bound" % (field_name, class_name) + return True + return False + def in_listed_classes(self, class_name): """ returns True if the class is in the list of required classes and it's not in the skip list @@ -1124,6 +1203,7 @@ def generate_code(self): self.head_file.close() self.doc_file.close() + def _pretty_print(self, diagnostics): print("====\nErrors in parsing headers:") severities=['Ignored', 'Note', 'Warning', 'Error', 'Fatal'] @@ -1426,6 +1506,7 @@ def main(): 'base_classes_to_skip': config.get(s, 'base_classes_to_skip'), 'abstract_classes': config.get(s, 'abstract_classes'), 'skip': config.get(s, 'skip'), + 'field': config.get(s, 'field') if config.has_option(s, 'field') else None, 'rename_functions': config.get(s, 'rename_functions'), 'rename_classes': config.get(s, 'rename_classes'), 'out_file': opts.out_file or config.get(s, 'prefix'), diff --git a/targets/lua/templates/public_field.c b/targets/lua/templates/public_field.c new file mode 100644 index 00000000..bfa3ff7c --- /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 +} \ No newline at end of file diff --git a/targets/spidermonkey/conversions.yaml b/targets/spidermonkey/conversions.yaml index 04323b8f..6388ddf6 100644 --- a/targets/spidermonkey/conversions.yaml +++ b/targets/spidermonkey/conversions.yaml @@ -5,6 +5,7 @@ definitions: sfunction: "js_${generator.prefix}_${class_name}_${func_name}" constructor: "js_${generator.prefix}_${class_name}_constructor" ctor: "js_${generator.prefix}_${class_name}_ctor" + public_field: "js_${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/spidermonkey/templates/constructor.c b/targets/spidermonkey/templates/constructor.c index bfcef2ea..3a613208 100644 --- a/targets/spidermonkey/templates/constructor.c +++ b/targets/spidermonkey/templates/constructor.c @@ -58,4 +58,4 @@ bool ${signature_name}(JSContext *cx, uint32_t argc, jsval *vp) ScriptingCore::getInstance()->executeFunctionWithOwner(OBJECT_TO_JSVAL(obj), "_ctor", args); return true; #end if -} +} \ No newline at end of file diff --git a/targets/spidermonkey/templates/public_field.c b/targets/spidermonkey/templates/public_field.c new file mode 100644 index 00000000..169e225e --- /dev/null +++ b/targets/spidermonkey/templates/public_field.c @@ -0,0 +1,60 @@ +## ===== member implementation template +bool ${signature_name}_get_${name}(JSContext *cx, uint32_t argc, jsval *vp) +{ + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + js_proxy_t *proxy = jsb_get_js_proxy(args.thisv().toObjectOrNull()); + ${namespaced_class_name}* cobj = (${namespaced_class_name} *)(proxy ? proxy->ptr : NULL); + JSB_PRECONDITION2( cobj, cx, false, "${signature_name}_get_${name} : Invalid Native Object"); + + #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, + "out_value": "jsval jsret" + })}; + #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, + "out_value": "jsval jsret" + })}; + #end if + args.rval().set(jsret); + return true; +} +bool ${signature_name}_set_${name}(JSContext *cx, uint32_t argc, jsval *vp) +{ + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + js_proxy_t *proxy = jsb_get_js_proxy(args.thisv().toObjectOrNull()); + ${namespaced_class_name}* cobj = (${namespaced_class_name} *)(proxy ? proxy->ptr : NULL); + JSB_PRECONDITION2( cobj, cx, false, "${signature_name}_set_${name} : Invalid Native Object"); + + bool ok = true; +#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, + "in_value": "args.get(0)", + "out_value": "arg0", + "func_name": $name, + "scriptname": $generator.scriptname_from_native($ntype.namespaced_name, $ntype.namespace_name), + "level": 2, + "arg":$ntype, + })}; + JSB_PRECONDITION2(ok, cx, false, "${signature_name}_set_${name} : Error processing new value"); +#if $ntype.is_object and not $ntype.object_can_convert($generator) + cobj->$pretty_name = *arg0; +#else + cobj->$pretty_name = arg0; +#end if + return true; +} diff --git a/targets/spidermonkey/templates/register.c b/targets/spidermonkey/templates/register.c index be8e47ea..fd760941 100644 --- a/targets/spidermonkey/templates/register.c +++ b/targets/spidermonkey/templates/register.c @@ -8,10 +8,11 @@ #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 #if len($current_class.parents) > 0 extern JSObject *jsb_${current_class.parents[0].underlined_class_name}_prototype; -#end if +#end if void js_${current_class.underlined_class_name}_finalize(JSFreeOp *fop, JSObject *obj) { CCLOGINFO("jsbindings: finalizing JS object %p (${current_class.class_name})", obj); #if (not $current_class.is_ref_class and $has_constructor) or $generator.script_control_cpp @@ -51,6 +52,11 @@ void js_register_${generator.prefix}_${current_class.class_name}(JSContext *cx, static JSPropertySpec properties[] = { JS_PSG("__nativeObj", js_is_native_obj, JSPROP_PERMANENT | JSPROP_ENUMERATE), +#for m in public_fields + #if $generator.should_bind_field($current_class.class_name, m.name) + JS_PSGS("${m.name}", ${m.signature_name}_get_${m.name}, ${m.signature_name}_set_${m.name}, JSPROP_PERMANENT | JSPROP_ENUMERATE), + #end if +#end for JS_PS_END };