Skip to content

Commit 067ef5d

Browse files
committed
Merge pull request #184 from pandamicro/v3
Add auto bindings for public members
2 parents 3edab5b + 8b57a68 commit 067ef5d

File tree

6 files changed

+250
-2
lines changed

6 files changed

+250
-2
lines changed

generator.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,23 @@ def get_whole_name(self, generator):
414414

415415
return name
416416

417+
def object_can_convert(self, generator, is_to_native = True):
418+
if self.is_object:
419+
keys = []
420+
if self.canonical_type != None:
421+
keys.append(self.canonical_type.name)
422+
keys.append(self.name)
423+
if is_to_native:
424+
to_native_dict = generator.config['conversions']['to_native']
425+
if NativeType.dict_has_key_re(to_native_dict, keys):
426+
return True
427+
else:
428+
from_native_dict = generator.config['conversions']['from_native']
429+
if NativeType.dict_has_key_re(from_native_dict, keys):
430+
return True
431+
432+
return False
433+
417434
def __str__(self):
418435
return self.canonical_type.whole_name if None != self.canonical_type else self.whole_name
419436

@@ -426,11 +443,34 @@ def __init__(self, cursor):
426443
self.location = cursor.location
427444
member_field_re = re.compile('m_(\w+)')
428445
match = member_field_re.match(self.name)
446+
self.signature_name = self.name
447+
self.ntype = NativeType.from_type(cursor.type)
429448
if match:
430449
self.pretty_name = match.group(1)
431450
else:
432451
self.pretty_name = self.name
433452

453+
@staticmethod
454+
def can_parse(ntype):
455+
if ntype.kind == cindex.TypeKind.POINTER:
456+
return False
457+
native_type = NativeType.from_type(ntype)
458+
if ntype.kind == cindex.TypeKind.UNEXPOSED and native_type.name != "std::string":
459+
return False
460+
return True
461+
462+
def generate_code(self, current_class = None, generator = None):
463+
gen = current_class.generator if current_class else generator
464+
config = gen.config
465+
466+
if config['definitions'].has_key('public_field'):
467+
tpl = Template(config['definitions']['public_field'],
468+
searchList=[current_class, self])
469+
self.signature_name = str(tpl)
470+
tpl = Template(file=os.path.join(gen.target, "templates", "public_field.c"),
471+
searchList=[current_class, self])
472+
gen.impl_file.write(str(tpl))
473+
434474
# return True if found default argument.
435475
def iterate_param_node(param_node, depth=1):
436476
for node in param_node.get_children():
@@ -674,6 +714,7 @@ def __init__(self, cursor, generator):
674714
self.namespaced_class_name = self.class_name
675715
self.parents = []
676716
self.fields = []
717+
self.public_fields = []
677718
self.methods = {}
678719
self.static_methods = {}
679720
self.generator = generator
@@ -778,6 +819,9 @@ def generate_code(self):
778819
if self.generator.script_type == "lua":
779820
for m in self.override_methods_clean():
780821
m['impl'].generate_code(self, is_override = True)
822+
for m in self.public_fields:
823+
if self.generator.should_bind_field(self.class_name, m.name):
824+
m.generate_code(self)
781825
# generate register section
782826
register = Template(file=os.path.join(self.generator.target, "templates", "register.c"),
783827
searchList=[{"current_class": self}])
@@ -850,6 +894,8 @@ def _process_node(self, cursor):
850894

851895
elif cursor.kind == cindex.CursorKind.FIELD_DECL:
852896
self.fields.append(NativeField(cursor))
897+
if self._current_visibility == cindex.AccessSpecifierKind.PUBLIC and NativeField.can_parse(cursor.type):
898+
self.public_fields.append(NativeField(cursor))
853899
elif cursor.kind == cindex.CursorKind.CXX_ACCESS_SPEC_DECL:
854900
self._current_visibility = cursor.get_access_specifier()
855901
elif cursor.kind == cindex.CursorKind.CXX_METHOD and cursor.get_availability() != cindex.AvailabilityKind.DEPRECATED:
@@ -936,6 +982,7 @@ def __init__(self, opts):
936982
self.impl_file = None
937983
self.head_file = None
938984
self.skip_classes = {}
985+
self.bind_fields = {}
939986
self.generated_classes = {}
940987
self.rename_functions = {}
941988
self.rename_classes = {}
@@ -973,6 +1020,16 @@ def __init__(self, opts):
9731020
self.skip_classes[class_name] = match.group(1).split(" ")
9741021
else:
9751022
raise Exception("invalid list of skip methods")
1023+
if opts['field']:
1024+
list_of_fields = re.split(",\n?", opts['field'])
1025+
for field in list_of_fields:
1026+
class_name, fields = field.split("::")
1027+
self.bind_fields[class_name] = []
1028+
match = re.match("\[([^]]+)\]", fields)
1029+
if match:
1030+
self.bind_fields[class_name] = match.group(1).split(" ")
1031+
else:
1032+
raise Exception("invalid list of bind fields")
9761033
if opts['rename_functions']:
9771034
list_of_function_renames = re.split(",\n?", opts['rename_functions'])
9781035
for rename in list_of_function_renames:
@@ -1031,6 +1088,28 @@ def should_skip(self, class_name, method_name, verbose=False):
10311088
print "%s will be accepted (%s, %s)" % (class_name, key, self.skip_classes[key])
10321089
return False
10331090

1091+
def should_bind_field(self, class_name, field_name, verbose=False):
1092+
if class_name == "*" and self.bind_fields.has_key("*"):
1093+
for func in self.bind_fields["*"]:
1094+
if re.match(func, method_name):
1095+
return True
1096+
else:
1097+
for key in self.bind_fields.iterkeys():
1098+
if key == "*" or re.match("^" + key + "$", class_name):
1099+
if verbose:
1100+
print "%s in bind_fields" % (class_name)
1101+
if len(self.bind_fields[key]) == 1 and self.bind_fields[key][0] == "*":
1102+
if verbose:
1103+
print "All public fields of %s will be bound" % (class_name)
1104+
return True
1105+
if field_name != None:
1106+
for field in self.bind_fields[key]:
1107+
if re.match(field, field_name):
1108+
if verbose:
1109+
print "Field %s of %s will be bound" % (field_name, class_name)
1110+
return True
1111+
return False
1112+
10341113
def in_listed_classes(self, class_name):
10351114
"""
10361115
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):
11241203
self.head_file.close()
11251204
self.doc_file.close()
11261205

1206+
11271207
def _pretty_print(self, diagnostics):
11281208
print("====\nErrors in parsing headers:")
11291209
severities=['Ignored', 'Note', 'Warning', 'Error', 'Fatal']
@@ -1426,6 +1506,7 @@ def main():
14261506
'base_classes_to_skip': config.get(s, 'base_classes_to_skip'),
14271507
'abstract_classes': config.get(s, 'abstract_classes'),
14281508
'skip': config.get(s, 'skip'),
1509+
'field': config.get(s, 'field') if config.has_option(s, 'field') else None,
14291510
'rename_functions': config.get(s, 'rename_functions'),
14301511
'rename_classes': config.get(s, 'rename_classes'),
14311512
'out_file': opts.out_file or config.get(s, 'prefix'),
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
## ===== member implementation template
2+
int ${signature_name}_get${name}(lua_State* tolua_S)
3+
{
4+
${namespaced_class_name}* cobj = nullptr;
5+
\#if COCOS2D_DEBUG >= 1
6+
tolua_Error tolua_err;
7+
if (!tolua_isusertype(tolua_S,1,"${generator.scriptname_from_native($namespaced_class_name, $namespace_name)}",0,&tolua_err)) goto tolua_lerror;
8+
\#endif
9+
10+
cobj = (${namespaced_class_name}*)tolua_tousertype(tolua_S,1,0);
11+
12+
\#if COCOS2D_DEBUG >= 1
13+
if (!cobj)
14+
{
15+
tolua_error(tolua_S,"invalid 'cobj' in function '${signature_name}_get${name}'", nullptr);
16+
return 0;
17+
}
18+
\#endif
19+
20+
#if $ntype.is_object and not $ntype.object_can_convert($generator, False)
21+
${ntype.from_native({"generator": $generator,
22+
"type_name": $ntype.namespaced_name.replace("*", ""),
23+
"ntype": $ntype.get_whole_name($generator)+"*",
24+
"level": 2,
25+
"scriptname": $generator.scriptname_from_native($ntype.namespaced_name, $ntype.namespace_name),
26+
"in_value":"&cobj->" + $pretty_name,
27+
})};
28+
#else
29+
${ntype.from_native({"generator": $generator,
30+
"type_name": $ntype.namespaced_name.replace("*", ""),
31+
"ntype": $ntype.get_whole_name($generator),
32+
"level": 2,
33+
"scriptname": $generator.scriptname_from_native($ntype.namespaced_name, $ntype.namespace_name),
34+
"in_value":"cobj->" + $pretty_name,
35+
})};
36+
#end if
37+
38+
return 1;
39+
\#if COCOS2D_DEBUG >= 1
40+
tolua_lerror:
41+
tolua_error(tolua_S,"#ferror in function '${signature_name}_get${name}'.",&tolua_err);
42+
return 0;
43+
\#endif
44+
}
45+
46+
int ${signature_name}_set${name}(lua_State* tolua_S)
47+
{
48+
int argc = 0;
49+
${namespaced_class_name}* cobj = nullptr;
50+
bool ok = true;
51+
52+
\#if COCOS2D_DEBUG >= 1
53+
tolua_Error tolua_err;
54+
if (!tolua_isusertype(tolua_S,1,"${generator.scriptname_from_native($namespaced_class_name, $namespace_name)}",0,&tolua_err)) goto tolua_lerror;
55+
\#endif
56+
57+
cobj = (${namespaced_class_name}*)tolua_tousertype(tolua_S,1,0);
58+
59+
\#if COCOS2D_DEBUG >= 1
60+
if (!cobj)
61+
{
62+
tolua_error(tolua_S,"invalid 'cobj' in function '${signature_name}_set${name}'", nullptr);
63+
return 0;
64+
}
65+
\#endif
66+
argc = lua_gettop(tolua_S) - 1;
67+
68+
if (1 == argc)
69+
{
70+
#if $ntype.is_object and not $ntype.object_can_convert($generator)
71+
${ntype.to_string($generator)}* arg0 = nullptr;
72+
#else
73+
${ntype.to_string($generator)} arg0;
74+
#end if
75+
${ntype.to_native({"generator": $generator,
76+
"arg_idx": 2,
77+
"out_value": "arg0",
78+
"lua_namespaced_class_name": $generator.scriptname_from_native($namespaced_class_name, $namespace_name),
79+
"func_name": $name,
80+
"scriptname": $generator.scriptname_from_native($ntype.namespaced_name, $ntype.namespace_name),
81+
"level": 2,
82+
"arg":$ntype,
83+
})};
84+
#if $ntype.is_object and not $ntype.object_can_convert($generator)
85+
cobj->$pretty_name = *arg0;
86+
#else
87+
cobj->$pretty_name = arg0;
88+
#end if
89+
return 0;
90+
}
91+
92+
CCLOG("%s has wrong number of arguments: %d, was expecting %d \n", "${generator.scriptname_from_native($namespaced_class_name, $namespace_name)}:${name}",argc, 1);
93+
return 0;
94+
95+
\#if COCOS2D_DEBUG >= 1
96+
tolua_lerror:
97+
tolua_error(tolua_S,"#ferror in function '${signature_name}_get${name}'.",&tolua_err);
98+
return 0;
99+
\#endif
100+
}

targets/spidermonkey/conversions.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ definitions:
55
sfunction: "js_${generator.prefix}_${class_name}_${func_name}"
66
constructor: "js_${generator.prefix}_${class_name}_constructor"
77
ctor: "js_${generator.prefix}_${class_name}_ctor"
8+
public_field: "js_${generator.prefix}_${class_name}"
89
conversions:
910
# some times you want to use a special native type when converting from spidermonkey to native
1011
# the most common case would be from JS-boolean to bool. Using "bool" will fail here since we

targets/spidermonkey/templates/constructor.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,4 @@ bool ${signature_name}(JSContext *cx, uint32_t argc, jsval *vp)
5858
ScriptingCore::getInstance()->executeFunctionWithOwner(OBJECT_TO_JSVAL(obj), "_ctor", args);
5959
return true;
6060
#end if
61-
}
61+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
## ===== member implementation template
2+
bool ${signature_name}_get_${name}(JSContext *cx, uint32_t argc, jsval *vp)
3+
{
4+
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
5+
js_proxy_t *proxy = jsb_get_js_proxy(args.thisv().toObjectOrNull());
6+
${namespaced_class_name}* cobj = (${namespaced_class_name} *)(proxy ? proxy->ptr : NULL);
7+
JSB_PRECONDITION2( cobj, cx, false, "${signature_name}_get_${name} : Invalid Native Object");
8+
9+
#if $ntype.is_object and not $ntype.object_can_convert($generator, False)
10+
${ntype.from_native({"generator": $generator,
11+
"type_name": $ntype.namespaced_name.replace("*", ""),
12+
"ntype": $ntype.get_whole_name($generator)+"*",
13+
"level": 2,
14+
"scriptname": $generator.scriptname_from_native($ntype.namespaced_name, $ntype.namespace_name),
15+
"in_value": "&cobj->" + $pretty_name,
16+
"out_value": "jsval jsret"
17+
})};
18+
#else
19+
${ntype.from_native({"generator": $generator,
20+
"type_name": $ntype.namespaced_name.replace("*", ""),
21+
"ntype": $ntype.get_whole_name($generator),
22+
"level": 2,
23+
"scriptname": $generator.scriptname_from_native($ntype.namespaced_name, $ntype.namespace_name),
24+
"in_value":"cobj->" + $pretty_name,
25+
"out_value": "jsval jsret"
26+
})};
27+
#end if
28+
args.rval().set(jsret);
29+
return true;
30+
}
31+
bool ${signature_name}_set_${name}(JSContext *cx, uint32_t argc, jsval *vp)
32+
{
33+
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
34+
js_proxy_t *proxy = jsb_get_js_proxy(args.thisv().toObjectOrNull());
35+
${namespaced_class_name}* cobj = (${namespaced_class_name} *)(proxy ? proxy->ptr : NULL);
36+
JSB_PRECONDITION2( cobj, cx, false, "${signature_name}_set_${name} : Invalid Native Object");
37+
38+
bool ok = true;
39+
#if $ntype.is_object and not $ntype.object_can_convert($generator)
40+
${ntype.to_string($generator)}* arg0 = nullptr;
41+
#else
42+
${ntype.to_string($generator)} arg0;
43+
#end if
44+
${ntype.to_native({"generator": $generator,
45+
"arg_idx": 2,
46+
"in_value": "args.get(0)",
47+
"out_value": "arg0",
48+
"func_name": $name,
49+
"scriptname": $generator.scriptname_from_native($ntype.namespaced_name, $ntype.namespace_name),
50+
"level": 2,
51+
"arg":$ntype,
52+
})};
53+
JSB_PRECONDITION2(ok, cx, false, "${signature_name}_set_${name} : Error processing new value");
54+
#if $ntype.is_object and not $ntype.object_can_convert($generator)
55+
cobj->$pretty_name = *arg0;
56+
#else
57+
cobj->$pretty_name = arg0;
58+
#end if
59+
return true;
60+
}

targets/spidermonkey/templates/register.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
#set generator = $current_class.generator
99
#set methods = $current_class.methods_clean()
1010
#set st_methods = $current_class.static_methods_clean()
11+
#set public_fields = $current_class.public_fields
1112
#if len($current_class.parents) > 0
1213
extern JSObject *jsb_${current_class.parents[0].underlined_class_name}_prototype;
13-
#end if
1414

15+
#end if
1516
void js_${current_class.underlined_class_name}_finalize(JSFreeOp *fop, JSObject *obj) {
1617
CCLOGINFO("jsbindings: finalizing JS object %p (${current_class.class_name})", obj);
1718
#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,
5152

5253
static JSPropertySpec properties[] = {
5354
JS_PSG("__nativeObj", js_is_native_obj, JSPROP_PERMANENT | JSPROP_ENUMERATE),
55+
#for m in public_fields
56+
#if $generator.should_bind_field($current_class.class_name, m.name)
57+
JS_PSGS("${m.name}", ${m.signature_name}_get_${m.name}, ${m.signature_name}_set_${m.name}, JSPROP_PERMANENT | JSPROP_ENUMERATE),
58+
#end if
59+
#end for
5460
JS_PS_END
5561
};
5662

0 commit comments

Comments
 (0)