bind: avoid ObjC reserved names
The new tests in CL 28494 exposed a bug: the ObjC generator does not avoid reserved names and names with special meaning ("init"). Generalize the name sanitizer from the Java generator and use that. Also, move the lowerFirst function to gen.go since it is now used by both generators. Change-Id: I25b7af2594b2ea136f05d2bab1cfdc66ba169859 Reviewed-on: https://go-review.googlesource.com/28592 Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
parent
7f59993615
commit
e99a906c3a
43
bind/gen.go
43
bind/gen.go
|
@ -11,6 +11,9 @@ import (
|
|||
"go/types"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -340,3 +343,43 @@ func constExactString(o *types.Const) string {
|
|||
// TODO: warning?
|
||||
return v.String()
|
||||
}
|
||||
|
||||
func lowerFirst(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var conv []rune
|
||||
for len(s) > 0 {
|
||||
r, n := utf8.DecodeRuneInString(s)
|
||||
if !unicode.IsUpper(r) {
|
||||
if l := len(conv); l > 1 {
|
||||
conv[l-1] = unicode.ToUpper(conv[l-1])
|
||||
}
|
||||
return string(conv) + s
|
||||
}
|
||||
conv = append(conv, unicode.ToLower(r))
|
||||
s = s[n:]
|
||||
}
|
||||
return string(conv)
|
||||
}
|
||||
|
||||
// newNameSanitizer returns a functions that replaces all dashes and dots
|
||||
// with underscores, as well as avoiding reserved words by suffixing such
|
||||
// identifiers with underscores.
|
||||
func newNameSanitizer(res []string) func(s string) string {
|
||||
reserved := make(map[string]bool)
|
||||
for _, word := range res {
|
||||
reserved[word] = true
|
||||
}
|
||||
symbols := strings.NewReplacer(
|
||||
"-", "_",
|
||||
".", "_",
|
||||
)
|
||||
return func(s string) string {
|
||||
if reserved[s] {
|
||||
return s + "_"
|
||||
}
|
||||
return symbols.Replace(s)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -375,7 +375,7 @@ func (g *JavaGen) genJNIFuncSignature(o *types.Func, sName string, proxy bool) {
|
|||
} else {
|
||||
g.Printf(g.className())
|
||||
}
|
||||
oName := javaNameReplacer.Replace(lowerFirst(o.Name()))
|
||||
oName := javaNameReplacer(lowerFirst(o.Name()))
|
||||
if strings.HasSuffix(oName, "_") {
|
||||
oName += "1" // JNI doesn't like methods ending with underscore, needs the _1 suffixing
|
||||
}
|
||||
|
@ -435,7 +435,7 @@ func (g *JavaGen) genFuncSignature(o *types.Func, static, header bool) {
|
|||
if !header {
|
||||
g.Printf("native ")
|
||||
}
|
||||
g.Printf("%s %s(", ret, javaNameReplacer.Replace(lowerFirst(o.Name())))
|
||||
g.Printf("%s %s(", ret, javaNameReplacer(lowerFirst(o.Name())))
|
||||
params := sig.Params()
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
if i > 0 {
|
||||
|
@ -564,51 +564,20 @@ func (g *JavaGen) gobindOpts() string {
|
|||
return strings.Join(opts, " ")
|
||||
}
|
||||
|
||||
var javaKeywordsAndReserves = []string{
|
||||
var javaNameReplacer = newNameSanitizer([]string{
|
||||
"abstract", "assert", "boolean", "break", "byte", "case", "catch", "char",
|
||||
"class", "const", "continue", "default", "do", "double", "else", "enum",
|
||||
"extends", "final", "finally", "float", "for", "goto", "if", "implements",
|
||||
"import", "instanceof", "int", "interface", "long", "native", "new", "package",
|
||||
"private", "protected", "public", "return", "short", "static", "strictfp",
|
||||
"super", "switch", "synchronized", "this", "throw", "throws", "transient",
|
||||
"try", "void", "volatile", "while", "false", "null", "true"}
|
||||
|
||||
// javaNameSanitizer replaces all dashes and dots with underscores, as well as
|
||||
// avoiding all keywords and reserved words by suffixing such identifier with
|
||||
// an underscore.
|
||||
type javaNameSanitizer struct {
|
||||
symbols *strings.Replacer
|
||||
reserved map[string]bool
|
||||
}
|
||||
|
||||
func newJavaNameSanitizer() *javaNameSanitizer {
|
||||
reserved := make(map[string]bool)
|
||||
for _, word := range javaKeywordsAndReserves {
|
||||
reserved[word] = true
|
||||
}
|
||||
return &javaNameSanitizer{
|
||||
symbols: strings.NewReplacer(
|
||||
"-", "_",
|
||||
".", "_",
|
||||
),
|
||||
reserved: reserved,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *javaNameSanitizer) Replace(s string) string {
|
||||
if r.reserved[s] {
|
||||
return s + "_"
|
||||
}
|
||||
return r.symbols.Replace(s)
|
||||
}
|
||||
|
||||
var javaNameReplacer = newJavaNameSanitizer()
|
||||
"try", "void", "volatile", "while", "false", "null", "true"})
|
||||
|
||||
func (g *JavaGen) javaPkgName(pkg *types.Package) string {
|
||||
if pkg == nil {
|
||||
return "go"
|
||||
}
|
||||
s := javaNameReplacer.Replace(pkg.Name())
|
||||
s := javaNameReplacer(pkg.Name())
|
||||
if g.JavaPkg != "" {
|
||||
return g.JavaPkg + "." + s
|
||||
} else {
|
||||
|
@ -624,7 +593,7 @@ func className(pkg *types.Package) string {
|
|||
if pkg == nil {
|
||||
return "Universe"
|
||||
}
|
||||
return javaNameReplacer.Replace(strings.Title(pkg.Name()))
|
||||
return javaNameReplacer(strings.Title(pkg.Name()))
|
||||
}
|
||||
|
||||
func (g *JavaGen) genConst(o *types.Const) {
|
||||
|
@ -1048,7 +1017,7 @@ func (g *JavaGen) GenC() error {
|
|||
jniParams += g.jniSigType(params.At(i).Type())
|
||||
}
|
||||
g.Printf("mid_%s_%s = (*env)->GetMethodID(env, clazz, %q, \"(%s)%s\");\n",
|
||||
iface.obj.Name(), m.Name(), javaNameReplacer.Replace(lowerFirst(m.Name())), jniParams, retSig)
|
||||
iface.obj.Name(), m.Name(), javaNameReplacer(lowerFirst(m.Name())), jniParams, retSig)
|
||||
}
|
||||
g.Printf("\n")
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@ import (
|
|||
"go/types"
|
||||
"math"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// TODO(hyangah): handle method name conflicts.
|
||||
|
@ -162,7 +160,7 @@ func (g *objcGen) genH() error {
|
|||
continue
|
||||
}
|
||||
objcType := g.objcType(obj.Type())
|
||||
g.Printf("+ (%s) %s;\n", objcType, lowerFirst(obj.Name()))
|
||||
g.Printf("+ (%s) %s;\n", objcType, objcNameReplacer(lowerFirst(obj.Name())))
|
||||
g.Printf("+ (void) set%s:(%s)v;\n", obj.Name(), objcType)
|
||||
g.Printf("\n")
|
||||
}
|
||||
|
@ -305,7 +303,7 @@ func (g *objcGen) genVarM(o *types.Var) {
|
|||
g.Printf("}\n\n")
|
||||
|
||||
// getter
|
||||
g.Printf("+ (%s) %s {\n", objcType, lowerFirst(o.Name()))
|
||||
g.Printf("+ (%s) %s {\n", objcType, objcNameReplacer(lowerFirst(o.Name())))
|
||||
g.Indent()
|
||||
g.Printf("%s r0 = ", g.cgoType(o.Type()))
|
||||
g.Printf("var_get%s_%s();\n", g.pkgPrefix, o.Name())
|
||||
|
@ -470,7 +468,7 @@ func (s *funcSummary) asMethod(g *objcGen) string {
|
|||
params = append(params, fmt.Sprintf("%s:(%s)%s", key, g.objcType(p.typ)+"*", p.name))
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("(%s)%s%s", s.ret, lowerFirst(s.name), strings.Join(params, " "))
|
||||
return fmt.Sprintf("(%s)%s%s", s.ret, objcNameReplacer(lowerFirst(s.name)), strings.Join(params, " "))
|
||||
}
|
||||
|
||||
func (s *funcSummary) callMethod(g *objcGen) string {
|
||||
|
@ -491,7 +489,7 @@ func (s *funcSummary) callMethod(g *objcGen) string {
|
|||
params = append(params, fmt.Sprintf("%s:&%s", key, p.name))
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s%s", lowerFirst(s.name), strings.Join(params, " "))
|
||||
return fmt.Sprintf("%s%s", objcNameReplacer(lowerFirst(s.name)), strings.Join(params, " "))
|
||||
}
|
||||
|
||||
func (s *funcSummary) returnsVal() bool {
|
||||
|
@ -522,7 +520,7 @@ func (g *objcGen) genFuncM(obj *types.Func) {
|
|||
|
||||
func (g *objcGen) genGetter(oName string, f *types.Var) {
|
||||
t := f.Type()
|
||||
g.Printf("- (%s)%s {\n", g.objcType(t), lowerFirst(f.Name()))
|
||||
g.Printf("- (%s)%s {\n", g.objcType(t), objcNameReplacer(lowerFirst(f.Name())))
|
||||
g.Indent()
|
||||
g.Printf("int32_t refnum = go_seq_go_to_refnum(self._ref);\n")
|
||||
g.Printf("%s r0 = ", g.cgoType(f.Type()))
|
||||
|
@ -885,7 +883,7 @@ func (g *objcGen) genStructH(obj *types.TypeName, t *types.Struct) {
|
|||
continue
|
||||
}
|
||||
name, typ := f.Name(), g.objcFieldType(f.Type())
|
||||
g.Printf("- (%s)%s;\n", typ, lowerFirst(name))
|
||||
g.Printf("- (%s)%s;\n", typ, objcNameReplacer(lowerFirst(name)))
|
||||
g.Printf("- (void)set%s:(%s)v;\n", name, typ)
|
||||
}
|
||||
|
||||
|
@ -896,7 +894,7 @@ func (g *objcGen) genStructH(obj *types.TypeName, t *types.Struct) {
|
|||
continue
|
||||
}
|
||||
s := g.funcSummary(m)
|
||||
g.Printf("- %s;\n", lowerFirst(s.asMethod(g)))
|
||||
g.Printf("- %s;\n", objcNameReplacer(lowerFirst(s.asMethod(g))))
|
||||
}
|
||||
g.Printf("@end\n")
|
||||
}
|
||||
|
@ -1050,25 +1048,10 @@ func (g *objcGen) objcType(typ types.Type) string {
|
|||
}
|
||||
}
|
||||
|
||||
func lowerFirst(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var conv []rune
|
||||
for len(s) > 0 {
|
||||
r, n := utf8.DecodeRuneInString(s)
|
||||
if !unicode.IsUpper(r) {
|
||||
if l := len(conv); l > 1 {
|
||||
conv[l-1] = unicode.ToUpper(conv[l-1])
|
||||
}
|
||||
return string(conv) + s
|
||||
}
|
||||
conv = append(conv, unicode.ToLower(r))
|
||||
s = s[n:]
|
||||
}
|
||||
return string(conv)
|
||||
}
|
||||
var objcNameReplacer = newNameSanitizer([]string{
|
||||
"void", "char", "short", "int", "long", "float", "double", "signed",
|
||||
"unsigned", "id", "const", "volatile", "in", "out", "inout", "bycopy",
|
||||
"byref", "oneway", "self", "super", "init"})
|
||||
|
||||
const (
|
||||
objcPreamble = `// Objective-C API for talking to %[1]s Go package.
|
||||
|
|
|
@ -20,29 +20,29 @@
|
|||
- (void)byte;
|
||||
- (void)case;
|
||||
- (void)catch;
|
||||
- (void)char;
|
||||
- (void)char_;
|
||||
- (void)class;
|
||||
- (void)const;
|
||||
- (void)const_;
|
||||
- (void)continue;
|
||||
- (void)default;
|
||||
- (void)do;
|
||||
- (void)double;
|
||||
- (void)double_;
|
||||
- (void)else;
|
||||
- (void)enum;
|
||||
- (void)extends;
|
||||
- (void)false;
|
||||
- (void)final;
|
||||
- (void)finally;
|
||||
- (void)float;
|
||||
- (void)float_;
|
||||
- (void)for;
|
||||
- (void)goto;
|
||||
- (void)if;
|
||||
- (void)implements;
|
||||
- (void)import;
|
||||
- (void)instanceof;
|
||||
- (void)int;
|
||||
- (void)int_;
|
||||
- (void)interface;
|
||||
- (void)long;
|
||||
- (void)long_;
|
||||
- (void)native;
|
||||
- (void)new;
|
||||
- (void)null;
|
||||
|
@ -51,10 +51,10 @@
|
|||
- (void)protected;
|
||||
- (void)public;
|
||||
- (void)return;
|
||||
- (void)short;
|
||||
- (void)short_;
|
||||
- (void)static;
|
||||
- (void)strictfp;
|
||||
- (void)super;
|
||||
- (void)super_;
|
||||
- (void)switch;
|
||||
- (void)synchronized;
|
||||
- (void)this;
|
||||
|
@ -63,8 +63,8 @@
|
|||
- (void)transient;
|
||||
- (void)true;
|
||||
- (void)try;
|
||||
- (void)void;
|
||||
- (void)volatile;
|
||||
- (void)void_;
|
||||
- (void)volatile_;
|
||||
- (void)while;
|
||||
@end
|
||||
|
||||
|
@ -82,29 +82,29 @@
|
|||
- (void)byte;
|
||||
- (void)case;
|
||||
- (void)catch;
|
||||
- (void)char;
|
||||
- (void)char_;
|
||||
- (void)class;
|
||||
- (void)const;
|
||||
- (void)const_;
|
||||
- (void)continue;
|
||||
- (void)default;
|
||||
- (void)do;
|
||||
- (void)double;
|
||||
- (void)double_;
|
||||
- (void)else;
|
||||
- (void)enum;
|
||||
- (void)extends;
|
||||
- (void)false;
|
||||
- (void)final;
|
||||
- (void)finally;
|
||||
- (void)float;
|
||||
- (void)float_;
|
||||
- (void)for;
|
||||
- (void)goto;
|
||||
- (void)if;
|
||||
- (void)implements;
|
||||
- (void)import;
|
||||
- (void)instanceof;
|
||||
- (void)int;
|
||||
- (void)int_;
|
||||
- (void)interface;
|
||||
- (void)long;
|
||||
- (void)long_;
|
||||
- (void)native;
|
||||
- (void)new;
|
||||
- (void)null;
|
||||
|
@ -113,10 +113,10 @@
|
|||
- (void)protected;
|
||||
- (void)public;
|
||||
- (void)return;
|
||||
- (void)short;
|
||||
- (void)short_;
|
||||
- (void)static;
|
||||
- (void)strictfp;
|
||||
- (void)super;
|
||||
- (void)super_;
|
||||
- (void)switch;
|
||||
- (void)synchronized;
|
||||
- (void)this;
|
||||
|
@ -125,8 +125,8 @@
|
|||
- (void)transient;
|
||||
- (void)true;
|
||||
- (void)try;
|
||||
- (void)void;
|
||||
- (void)volatile;
|
||||
- (void)void_;
|
||||
- (void)volatile_;
|
||||
- (void)while;
|
||||
@end
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ static NSString* errDomain = @"go.keywords";
|
|||
proxykeywords_KeywordCaller_Catch(refnum);
|
||||
}
|
||||
|
||||
- (void)char {
|
||||
- (void)char_ {
|
||||
int32_t refnum = go_seq_go_to_refnum(self._ref);
|
||||
proxykeywords_KeywordCaller_Char(refnum);
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ static NSString* errDomain = @"go.keywords";
|
|||
proxykeywords_KeywordCaller_Class(refnum);
|
||||
}
|
||||
|
||||
- (void)const {
|
||||
- (void)const_ {
|
||||
int32_t refnum = go_seq_go_to_refnum(self._ref);
|
||||
proxykeywords_KeywordCaller_Const(refnum);
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ static NSString* errDomain = @"go.keywords";
|
|||
proxykeywords_KeywordCaller_Do(refnum);
|
||||
}
|
||||
|
||||
- (void)double {
|
||||
- (void)double_ {
|
||||
int32_t refnum = go_seq_go_to_refnum(self._ref);
|
||||
proxykeywords_KeywordCaller_Double(refnum);
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ static NSString* errDomain = @"go.keywords";
|
|||
proxykeywords_KeywordCaller_Finally(refnum);
|
||||
}
|
||||
|
||||
- (void)float {
|
||||
- (void)float_ {
|
||||
int32_t refnum = go_seq_go_to_refnum(self._ref);
|
||||
proxykeywords_KeywordCaller_Float(refnum);
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ static NSString* errDomain = @"go.keywords";
|
|||
proxykeywords_KeywordCaller_Instanceof(refnum);
|
||||
}
|
||||
|
||||
- (void)int {
|
||||
- (void)int_ {
|
||||
int32_t refnum = go_seq_go_to_refnum(self._ref);
|
||||
proxykeywords_KeywordCaller_Int(refnum);
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ static NSString* errDomain = @"go.keywords";
|
|||
proxykeywords_KeywordCaller_Interface(refnum);
|
||||
}
|
||||
|
||||
- (void)long {
|
||||
- (void)long_ {
|
||||
int32_t refnum = go_seq_go_to_refnum(self._ref);
|
||||
proxykeywords_KeywordCaller_Long(refnum);
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ static NSString* errDomain = @"go.keywords";
|
|||
proxykeywords_KeywordCaller_Return(refnum);
|
||||
}
|
||||
|
||||
- (void)short {
|
||||
- (void)short_ {
|
||||
int32_t refnum = go_seq_go_to_refnum(self._ref);
|
||||
proxykeywords_KeywordCaller_Short(refnum);
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ static NSString* errDomain = @"go.keywords";
|
|||
proxykeywords_KeywordCaller_Strictfp(refnum);
|
||||
}
|
||||
|
||||
- (void)super {
|
||||
- (void)super_ {
|
||||
int32_t refnum = go_seq_go_to_refnum(self._ref);
|
||||
proxykeywords_KeywordCaller_Super(refnum);
|
||||
}
|
||||
|
@ -269,12 +269,12 @@ static NSString* errDomain = @"go.keywords";
|
|||
proxykeywords_KeywordCaller_Try(refnum);
|
||||
}
|
||||
|
||||
- (void)void {
|
||||
- (void)void_ {
|
||||
int32_t refnum = go_seq_go_to_refnum(self._ref);
|
||||
proxykeywords_KeywordCaller_Void(refnum);
|
||||
}
|
||||
|
||||
- (void)volatile {
|
||||
- (void)volatile_ {
|
||||
int32_t refnum = go_seq_go_to_refnum(self._ref);
|
||||
proxykeywords_KeywordCaller_Volatile(refnum);
|
||||
}
|
||||
|
@ -340,7 +340,7 @@ void cproxykeywords_KeywordCaller_Catch(int32_t refnum) {
|
|||
void cproxykeywords_KeywordCaller_Char(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
GoKeywordsKeywordCaller* o = go_seq_objc_from_refnum(refnum);
|
||||
[o char];
|
||||
[o char_];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,7 +354,7 @@ void cproxykeywords_KeywordCaller_Class(int32_t refnum) {
|
|||
void cproxykeywords_KeywordCaller_Const(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
GoKeywordsKeywordCaller* o = go_seq_objc_from_refnum(refnum);
|
||||
[o const];
|
||||
[o const_];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,7 +382,7 @@ void cproxykeywords_KeywordCaller_Do(int32_t refnum) {
|
|||
void cproxykeywords_KeywordCaller_Double(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
GoKeywordsKeywordCaller* o = go_seq_objc_from_refnum(refnum);
|
||||
[o double];
|
||||
[o double_];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,7 +431,7 @@ void cproxykeywords_KeywordCaller_Finally(int32_t refnum) {
|
|||
void cproxykeywords_KeywordCaller_Float(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
GoKeywordsKeywordCaller* o = go_seq_objc_from_refnum(refnum);
|
||||
[o float];
|
||||
[o float_];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,7 +480,7 @@ void cproxykeywords_KeywordCaller_Instanceof(int32_t refnum) {
|
|||
void cproxykeywords_KeywordCaller_Int(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
GoKeywordsKeywordCaller* o = go_seq_objc_from_refnum(refnum);
|
||||
[o int];
|
||||
[o int_];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -494,7 +494,7 @@ void cproxykeywords_KeywordCaller_Interface(int32_t refnum) {
|
|||
void cproxykeywords_KeywordCaller_Long(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
GoKeywordsKeywordCaller* o = go_seq_objc_from_refnum(refnum);
|
||||
[o long];
|
||||
[o long_];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -557,7 +557,7 @@ void cproxykeywords_KeywordCaller_Return(int32_t refnum) {
|
|||
void cproxykeywords_KeywordCaller_Short(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
GoKeywordsKeywordCaller* o = go_seq_objc_from_refnum(refnum);
|
||||
[o short];
|
||||
[o short_];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -578,7 +578,7 @@ void cproxykeywords_KeywordCaller_Strictfp(int32_t refnum) {
|
|||
void cproxykeywords_KeywordCaller_Super(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
GoKeywordsKeywordCaller* o = go_seq_objc_from_refnum(refnum);
|
||||
[o super];
|
||||
[o super_];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -641,14 +641,14 @@ void cproxykeywords_KeywordCaller_Try(int32_t refnum) {
|
|||
void cproxykeywords_KeywordCaller_Void(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
GoKeywordsKeywordCaller* o = go_seq_objc_from_refnum(refnum);
|
||||
[o void];
|
||||
[o void_];
|
||||
}
|
||||
}
|
||||
|
||||
void cproxykeywords_KeywordCaller_Volatile(int32_t refnum) {
|
||||
@autoreleasepool {
|
||||
GoKeywordsKeywordCaller* o = go_seq_objc_from_refnum(refnum);
|
||||
[o volatile];
|
||||
[o volatile_];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue