Skip to content

Commit

Permalink
Merge pull request #184 from pandamicro/v3
Browse files Browse the repository at this point in the history
Add auto bindings for public members
  • Loading branch information
pandamicro committed Jul 30, 2015
2 parents 3edab5b + 8b57a68 commit 067ef5d
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 2 deletions.
81 changes: 81 additions & 0 deletions generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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():
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}])
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 = {}
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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']
Expand Down Expand Up @@ -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'),
Expand Down
100 changes: 100 additions & 0 deletions targets/lua/templates/public_field.c
Original file line number Diff line number Diff line change
@@ -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
}
1 change: 1 addition & 0 deletions targets/spidermonkey/conversions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion targets/spidermonkey/templates/constructor.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
60 changes: 60 additions & 0 deletions targets/spidermonkey/templates/public_field.c
Original file line number Diff line number Diff line change
@@ -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;
}
8 changes: 7 additions & 1 deletion targets/spidermonkey/templates/register.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
};

Expand Down

0 comments on commit 067ef5d

Please sign in to comment.