Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add auto bindings for public members #184

Merged
merged 2 commits into from
Jul 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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