From e974939dfcd133911adc47594719e9b30140fb94 Mon Sep 17 00:00:00 2001 From: "nick." <64551534+null-nick@users.noreply.github.com> Date: Sat, 8 Aug 2026 12:09:02 +0200 Subject: [PATCH 1/5] Fix wire-usage filtering for TL parameter extraction Hardens extractParams against dropping genuine protocol fields: filter out client-only fields not present on the wire, broaden the wire-usage regex to cover assignment-first reads, typed Vector variants, and hasFlag assignment targets, normalize field names before matching against the filter, and fix legacy-suffix/bool-flag type detection. --- android/extract_object.go | 17 ++++++++++++++++- android/extract_params.go | 36 ++++++++++++++++++++++++++++++++---- consts/consts.go | 1 + 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/android/extract_object.go b/android/extract_object.go index ffc73e7..1542a2f 100644 --- a/android/extract_object.go +++ b/android/extract_object.go @@ -97,7 +97,11 @@ func extractObject(class *javaTypes.RawClass) (types.TLInterface, error) { } if len(packageName) == 0 { if isMethod { - packageName = "messages" + if containerNamespace := methodNamespaceFromPrefix(class.Prefix); len(containerNamespace) > 0 { + packageName = containerNamespace + } else { + packageName = "messages" + } } } else { packageName = strings.ToLower(packageName) @@ -136,3 +140,14 @@ func extractObject(class *javaTypes.RawClass) (types.TLInterface, error) { }, nil } } + +func methodNamespaceFromPrefix(prefix string) string { + if prefix == "TLRPC" || prefix == "TL" { + return "" + } + trimmed := strings.TrimPrefix(prefix, "TL_") + if trimmed == prefix || len(trimmed) == 0 { + return "" + } + return strings.ToLower(trimmed) +} diff --git a/android/extract_params.go b/android/extract_params.go index 95896f8..34ddf93 100644 --- a/android/extract_params.go +++ b/android/extract_params.go @@ -33,6 +33,7 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes compileUnVector := regexp.MustCompile(`Vector<(.*?)>`) compileUnknownVectorType := regexp.MustCompile(`\(\((.*?)\).*get`) dialogResolver := regexp.MustCompile(`DialogObject\..+\(`) + compileWireUsage := regexp.MustCompile(`(outputSerializedData\.write\w*\([^)]*\bthis\.(\w+)\b|inputSerializedData\.read\w*\([^)]*\)[^;]*\bthis\.(\w+)\s*=|this\.(\w+)\s*=\s*inputSerializedData\.read\w*\([^)]*\)|this\.(\w+)\.serializeToStream\(|this\.(\w+)\s*=\s*[^;]*\.TLdeserialize\(|Vector\.(?:de)?serialize\w*\([^)]*\bthis\.(\w+)\b|this\.(\w+)\s*=\s*(?:TLObject\.)?hasFlag\(|(?:TLObject\.)?setFlag\([^)]*\bthis\.(\w+)\b)`) for pos, line := range class.Content { if dialogResolver.MatchString(line.Line) { continue @@ -103,7 +104,7 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes var parameter schemeTypes.Parameter var fromBuffer bool parameter.Name = matches[0][2] - if matchedType := compileVarBuffer.FindAllStringSubmatch(line.Line, -1); len(matchedType) > 0 { + if matchedType := compileVarBuffer.FindAllStringSubmatch(line.Line, -1); len(matchedType) > 0 && !compileFlags.MatchString(line.Line) { parameter.Type = java.ParseType(matchedType[0][6]) fromBuffer = true } else if declaredType, ok := class.Vars[matches[0][2]]; ok { @@ -111,7 +112,7 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes } else if compileVarFlag.MatchString(line.Line) { parameter.Type = "int" } else if compileVarBool.MatchString(line.Line) { - parameter.Type = "bool" + parameter.Type = "Bool" } else if strings.HasPrefix(matches[0][1], "tLRPC") { escapedVar := regexp.QuoteMeta(matches[0][1]) compileReverseName := regexp.MustCompile(fmt.Sprintf("(%s =|this\\.)(\\w+)(;| = %s)", escapedVar, escapedVar)) @@ -159,7 +160,7 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes if flagValue == -1 { return nil, consts.FlagNotFound } - if !fromBuffer && parameter.Type == "Bool" { + if !fromBuffer && strings.EqualFold(parameter.Type, "Bool") { parameter.Type = "true" } @@ -199,5 +200,32 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes break } } - return params, nil + return filterNonWireParams(params, class.Content, declarationPos, compileWireUsage), nil +} + +func filterNonWireParams(params []schemeTypes.Parameter, content []javaTypes.LineInfo, declarationPos int, wireUsage *regexp.Regexp) []schemeTypes.Parameter { + confirmed := make(map[string]bool) + for pos, line := range content { + if pos <= declarationPos { + continue + } + if pos > declarationPos && line.Nesting == 1 { + break + } + for _, match := range wireUsage.FindAllStringSubmatch(line.Line, -1) { + for _, name := range match[1:] { + if len(name) > 0 { + confirmed[fixParamName(name)] = true + } + } + } + } + var filtered []schemeTypes.Parameter + flagNameRe := regexp.MustCompile(`^flags[0-9]*$`) + for _, p := range params { + if flagNameRe.MatchString(p.Name) || confirmed[p.Name] { + filtered = append(filtered, p) + } + } + return filtered } diff --git a/consts/consts.go b/consts/consts.go index a6f6891..41ae883 100644 --- a/consts/consts.go +++ b/consts/consts.go @@ -87,6 +87,7 @@ var ( regexp.MustCompile(`^secret$`), regexp.MustCompile(`Layer[0-9]+$`), regexp.MustCompile(`^TL_messages\.SendEncryptedMultiMedia$`), + regexp.MustCompile(`(?i)legacy$`), } BrokenNames = map[*regexp.Regexp]string{ regexp.MustCompile(`^((?Pis_admin)|is_(?P.*))$`): "$first$second", From baafd510c2b4b70dcac5c15665f5be8d8a9ee57d Mon Sep 17 00:00:00 2001 From: "nick." <64551534+null-nick@users.noreply.github.com> Date: Sat, 8 Aug 2026 12:19:48 +0200 Subject: [PATCH 2/5] Fix Vector.serialize/deserialize wire-usage detection for two more shapes The Vector alternative in compileWireUsage stopped at the first closing paren, so it missed this.X when a nested call sat between the buffer arg and the field (e.g. the lambda-based writer Vector.serialize(outputSerializedData, new Vector$$ExternalSyntheticLambda7(...), this.blocks)). Extend the match to the trailing ");" instead. Also add the assignment-first shape this.X = Vector.deserialize*(...), the dominant read-side pattern (634 occurrences in TLRPC.java) that the regex never covered. Caught via regression: updateGroupCallChainBlocks lost its `blocks` field and stories.report lost `id` (no readParams counterpart, so no other qualifying usage). Verified against the full corpus: 2 fields recovered, 0 dropped, no other method affected. --- android/extract_params.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/extract_params.go b/android/extract_params.go index 34ddf93..90b7864 100644 --- a/android/extract_params.go +++ b/android/extract_params.go @@ -33,7 +33,7 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes compileUnVector := regexp.MustCompile(`Vector<(.*?)>`) compileUnknownVectorType := regexp.MustCompile(`\(\((.*?)\).*get`) dialogResolver := regexp.MustCompile(`DialogObject\..+\(`) - compileWireUsage := regexp.MustCompile(`(outputSerializedData\.write\w*\([^)]*\bthis\.(\w+)\b|inputSerializedData\.read\w*\([^)]*\)[^;]*\bthis\.(\w+)\s*=|this\.(\w+)\s*=\s*inputSerializedData\.read\w*\([^)]*\)|this\.(\w+)\.serializeToStream\(|this\.(\w+)\s*=\s*[^;]*\.TLdeserialize\(|Vector\.(?:de)?serialize\w*\([^)]*\bthis\.(\w+)\b|this\.(\w+)\s*=\s*(?:TLObject\.)?hasFlag\(|(?:TLObject\.)?setFlag\([^)]*\bthis\.(\w+)\b)`) + compileWireUsage := regexp.MustCompile(`(outputSerializedData\.write\w*\([^)]*\bthis\.(\w+)\b|inputSerializedData\.read\w*\([^)]*\)[^;]*\bthis\.(\w+)\s*=|this\.(\w+)\s*=\s*inputSerializedData\.read\w*\([^)]*\)|this\.(\w+)\.serializeToStream\(|this\.(\w+)\s*=\s*[^;]*\.TLdeserialize\(|Vector\.(?:de)?serialize\w*\([^;]*?\bthis\.(\w+)\s*\)\s*;|this\.(\w+)\s*=\s*Vector\.(?:de)?serialize\w*\(|this\.(\w+)\s*=\s*(?:TLObject\.)?hasFlag\(|(?:TLObject\.)?setFlag\([^)]*\bthis\.(\w+)\b)`) for pos, line := range class.Content { if dialogResolver.MatchString(line.Line) { continue From 2e860d3261eb2fa16542c3e4ccf742454847e8fe Mon Sep 17 00:00:00 2001 From: "nick." <64551534+null-nick@users.noreply.github.com> Date: Sat, 8 Aug 2026 15:53:59 +0200 Subject: [PATCH 3/5] fix(java): resolve inherited fields/methods across cross-file hierarchies ParseClass split parent refs only on "$" (same-file nesting). Dotted cross-file refs like TLRPC.TL_messages_editMessage fell through unsplit, never matched tempList's "prefix$name" key, left ParentLink nil, and hid inherited field declarations from extractParams. extractObject also required deserializeResponse(T) in the class's own body to classify it as an RPC method. A class inheriting that method from a concrete parent (not just TLObject/TLMethod stubs) was misclassified as a TLConstructor and lost its namespace prefix. Fixes TL_ephemeral's TL_editMessage, which previously failed extraction outright. --- android/extract_object.go | 31 +++++++++++++++++++++++++++++++ java/parse_class.go | 17 +++++++++++------ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/android/extract_object.go b/android/extract_object.go index 1542a2f..d7593d9 100644 --- a/android/extract_object.go +++ b/android/extract_object.go @@ -64,6 +64,12 @@ func extractObject(class *javaTypes.RawClass) (types.TLInterface, error) { } } } + if !isMethod { + if inheritedMethod, inheritedResult := findInheritedMethodResult(class.ParentLink); inheritedMethod { + isMethod = true + methodResult = inheritedResult + } + } var deserializedParams, serializedParams []types.Parameter if serializePos != 0 { params, err := extractParams(class, serializePos) @@ -151,3 +157,28 @@ func methodNamespaceFromPrefix(prefix string) string { } return strings.ToLower(trimmed) } + +func findInheritedMethodResult(class *javaTypes.RawClass) (bool, string) { + if class == nil { + return false, "" + } + if class.Prefix == "TLObject" || class.Prefix == "TLMethod" { + return false, "" + } + compileResult := regexp.MustCompile(`(return|=) *(.*?)\.TLdeserialize`) + for _, line := range class.Content { + if java.CheckMethodDec(line, "deserializeResponse") || java.CheckMethodDec(line, "deserializeResponseT") { + for _, resultLine := range class.Content { + if matches := compileResult.FindAllStringSubmatch(resultLine.Line, -1); len(matches) > 0 { + formattedType, err := java.FormatType(matches[0][2], true) + if err != nil { + return true, "" + } + return true, formattedType + } + } + return true, "" + } + } + return findInheritedMethodResult(class.ParentLink) +} diff --git a/java/parse_class.go b/java/parse_class.go index e1a3f7d..95f8128 100644 --- a/java/parse_class.go +++ b/java/parse_class.go @@ -27,15 +27,20 @@ func ParseClass(name, content string) (*types.RawClass, error) { tlName.Content = parseLines(content) for _, line := range tlName.Content { if className := GetParentClass(line); len(className) > 0 { - tlName.ParentClass, err = FormatType(className, false) + parentPrefix := "" + parentName := className + if parentData := strings.Split(className, "$"); len(parentData) > 1 { + parentPrefix = parentData[0] + parentName = parentData[1] + } else if parentData := strings.Split(className, "."); len(parentData) > 1 { + parentPrefix = parentData[0] + parentName = parentData[len(parentData)-1] + } + tlName.ParentClass, err = FormatType(parentName, false) if err != nil { return nil, err } - if parentData := strings.Split(className, "$"); len(parentData) > 1 { - tlName.ParentPrefix = parentData[0] - } else { - tlName.ParentPrefix = "" - } + tlName.ParentPrefix = parentPrefix } } return &tlName, nil From cf0c7c499d6d63e52593f3d30d408dc08fd9dcb0 Mon Sep 17 00:00:00 2001 From: "nick." <64551534+null-nick@users.noreply.github.com> Date: Sat, 8 Aug 2026 17:04:51 +0200 Subject: [PATCH 4/5] Fixed Cross-File Type References and Two-Level Wire Field Detection - FormatType now resolves dotted Java references (TLRPC.TL_authorization) - SplitClasses guards field-name collisions against class-name rewrites - extractParams recovers wire fields nested one level under this.X.Y --- android/extract_params.go | 34 +++++++++++++++++++++++++++++++++- java/format_type.go | 7 +++++++ java/split_classes.go | 27 +++++++++++++++++++++++++-- 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/android/extract_params.go b/android/extract_params.go index 90b7864..0349fb1 100644 --- a/android/extract_params.go +++ b/android/extract_params.go @@ -33,7 +33,7 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes compileUnVector := regexp.MustCompile(`Vector<(.*?)>`) compileUnknownVectorType := regexp.MustCompile(`\(\((.*?)\).*get`) dialogResolver := regexp.MustCompile(`DialogObject\..+\(`) - compileWireUsage := regexp.MustCompile(`(outputSerializedData\.write\w*\([^)]*\bthis\.(\w+)\b|inputSerializedData\.read\w*\([^)]*\)[^;]*\bthis\.(\w+)\s*=|this\.(\w+)\s*=\s*inputSerializedData\.read\w*\([^)]*\)|this\.(\w+)\.serializeToStream\(|this\.(\w+)\s*=\s*[^;]*\.TLdeserialize\(|Vector\.(?:de)?serialize\w*\([^;]*?\bthis\.(\w+)\s*\)\s*;|this\.(\w+)\s*=\s*Vector\.(?:de)?serialize\w*\(|this\.(\w+)\s*=\s*(?:TLObject\.)?hasFlag\(|(?:TLObject\.)?setFlag\([^)]*\bthis\.(\w+)\b)`) + compileWireUsage := regexp.MustCompile(`(outputSerializedData\.write\w*\(this\.\w+\.(\w+)\)|outputSerializedData\.write\w*\([^)]*\bthis\.(\w+)\b|inputSerializedData\.read\w*\([^)]*\)[^;]*\bthis\.(\w+)\s*=|this\.(\w+)\s*=\s*inputSerializedData\.read\w*\([^)]*\)|this\.(\w+)\.serializeToStream\(|this\.(\w+)\s*=\s*[^;]*\.TLdeserialize\(|Vector\.(?:de)?serialize\w*\([^;]*?\bthis\.(\w+)\s*\)\s*;|this\.(\w+)\s*=\s*Vector\.(?:de)?serialize\w*\(|this\.(\w+)\s*=\s*(?:TLObject\.)?hasFlag\(|(?:TLObject\.)?setFlag\([^)]*\bthis\.(\w+)\b)`) for pos, line := range class.Content { if dialogResolver.MatchString(line.Line) { continue @@ -200,9 +200,41 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes break } } + params = append(params, findTwoLevelWireFields(params, class.Content, declarationPos)...) return filterNonWireParams(params, class.Content, declarationPos, compileWireUsage), nil } +func findTwoLevelWireFields(params []schemeTypes.Parameter, content []javaTypes.LineInfo, declarationPos int) []schemeTypes.Parameter { + existing := make(map[string]bool) + for _, p := range params { + existing[p.Name] = true + } + compileTwoLevelWrite := regexp.MustCompile(`outputSerializedData\.write(Int32|Int64|Bool|String|ByteArray|Double)\(this\.\w+\.(\w+)\)`) + writeTypeToTL := map[string]string{ + "Int32": "int", "Int64": "long", "Bool": "Bool", "String": "string", "ByteArray": "bytes", "Double": "double", + } + var found []schemeTypes.Parameter + seen := make(map[string]bool) + for pos, line := range content { + if pos <= declarationPos { + continue + } + if pos > declarationPos && line.Nesting == 1 { + break + } + matches := compileTwoLevelWrite.FindAllStringSubmatch(line.Line, -1) + for _, m := range matches { + name := fixParamName(m[2]) + if existing[name] || seen[name] { + continue + } + seen[name] = true + found = append(found, schemeTypes.Parameter{Name: name, Type: writeTypeToTL[m[1]]}) + } + } + return found +} + func filterNonWireParams(params []schemeTypes.Parameter, content []javaTypes.LineInfo, declarationPos int, wireUsage *regexp.Regexp) []schemeTypes.Parameter { confirmed := make(map[string]bool) for pos, line := range content { diff --git a/java/format_type.go b/java/format_type.go index 9f12426..5440d42 100644 --- a/java/format_type.go +++ b/java/format_type.go @@ -22,6 +22,13 @@ func FormatType(name string, clearTLName bool) (string, error) { } fileName := strings.Split(name, "$") name = fileName[len(fileName)-1] + if !strings.ContainsAny(name, "<>") { + if dotParts := strings.SplitN(name, ".", 2); len(dotParts) > 1 && len(dotParts[0]) > 0 { + if first := dotParts[0][0]; first >= 'A' && first <= 'Z' { + name = dotParts[1] + } + } + } if clearTLName { for _, prefix := range []string{"TL", "Tl", "_"} { name = strings.TrimPrefix(name, prefix) diff --git a/java/split_classes.go b/java/split_classes.go index a9244bf..4032c1f 100644 --- a/java/split_classes.go +++ b/java/split_classes.go @@ -40,6 +40,8 @@ func SplitClasses(className, content string, replaceClasses []string) map[string classNames = append(classNames, strings.Split(name, "$")[1]) } namesJoined := regexp.MustCompile(fmt.Sprintf(`(\b)(%s)(\b)`, strings.Join(classNames, "|"))) + thisFieldAccess := regexp.MustCompile(`this\.\w+$`) + fieldDeclaration := regexp.MustCompile(`^public [\w<>.\[\]$]+ (\w+)( =.*)?;$`) var dynamicRegex *regexp.Regexp replaceNames := make(map[string]string) appendName := func(base, name string) { @@ -51,8 +53,29 @@ func SplitClasses(className, content string, replaceClasses []string) map[string replaceNames = make(map[string]string) for i, line := range classLines { line = compileParentClasses.ReplaceAllString(line, `$1$$$2`) - if namesJoined.MatchString(line) { - line = namesJoined.ReplaceAllString(line, fmt.Sprintf("${1}%s$$$2$3", className)) + declaredFieldName := "" + if declMatches := fieldDeclaration.FindStringSubmatch(strings.TrimSpace(line)); declMatches != nil { + declaredFieldName = declMatches[1] + } + if idxs := namesJoined.FindAllStringSubmatchIndex(line, -1); len(idxs) > 0 { + var b strings.Builder + lastEnd := 0 + for _, idx := range idxs { + matchStart, matchEnd := idx[0], idx[1] + matchedName := line[idx[4]:idx[5]] + if thisFieldAccess.MatchString(line[:matchEnd]) || matchedName == declaredFieldName { + continue + } + b.WriteString(line[lastEnd:matchStart]) + b.WriteString(line[idx[2]:idx[3]]) + b.WriteString(className) + b.WriteString("$") + b.WriteString(line[idx[4]:idx[5]]) + b.WriteString(line[idx[6]:idx[7]]) + lastEnd = matchEnd + } + b.WriteString(line[lastEnd:]) + line = b.String() } if matches := compileClassInitializer2.FindAllStringSubmatch(line, -1); len(matches) > 0 { appendName(matches[0][1], matches[0][2]) From 95e8e6a100e700832a424436b0e5ff106973c3aa Mon Sep 17 00:00:00 2001 From: "nick." <64551534+null-nick@users.noreply.github.com> Date: Tue, 11 Aug 2026 01:02:40 +0200 Subject: [PATCH 5/5] fix: restore upstream param extraction, drop broken wire-usage filter - Removed filterNonWireParams/findTwoLevelWireFields/compileWireUsage: these were dropping genuine wire fields (e.g. forumTopic.peer) - compileVars: make $ optional in TLdeserialize pattern so it matches Peer.TLdeserialize, DraftMessage.TLdeserialize, etc. - Skip captured field names containing '(' or '$' (method call artifacts) - Skip 'this.X !=' comparison matches outside setFlag/hasFlag contexts (e.g. ephemeralReceiverBotId) - MergeParameters: preserve non-flagged params from deserialized path when missing from serialized, not just flagged ones --- android/extract_params.go | 73 ++++++--------------------------------- utils/merge_parameters.go | 6 ++-- 2 files changed, 14 insertions(+), 65 deletions(-) diff --git a/android/extract_params.go b/android/extract_params.go index 0349fb1..381a4c2 100644 --- a/android/extract_params.go +++ b/android/extract_params.go @@ -23,7 +23,7 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes var flagName string flagValue := -1 //fastCheck := regexp.MustCompile(`this\.\w+`) - compileVars := regexp.MustCompile(`\(?(this|tLRPC[^.]+)\.([^. )]+)( \?|\.\w+Value\(\)|\.add|\.get|\.serialize|\)| !| = (Boolean\.valueOf\(abstractSerializedData|abstractSerializedData|inputSerializedData|i[0-9+]*;|read|TLdeserialize;|Vector\.deserialize|\([^(]|\w+\$\w+\.\w+deserialize))\)?`) + compileVars := regexp.MustCompile(`\(?(this|tLRPC[^.]+)\.([^. )]+)( \?|\.\w+Value\(\)|\.add|\.get|\.serialize|\)| !| = (Boolean\.valueOf\(abstractSerializedData|abstractSerializedData|inputSerializedData|i[0-9+]*;|read|TLdeserialize;|Vector\.deserialize|\([^(]|\w+(\$\w+)?\.\w+deserialize))\)?`) compileVarBuffer := regexp.MustCompile(`^(this|tLRPC\$[^.]+)*\.*\w* *=* *((Boolean\.valueOf\()?(abstractSerializedData|inputSerializedData)[0-9]*|)?(\.write|\.read|TLRPC\$)([^(.]+).*?\);`) compileVarFlag := regexp.MustCompile(`this\.flags[0-9]* = readInt[0-9]+;`) compileVarBool := regexp.MustCompile(`this\.\w+ = \([^)]*readInt32[0-9]*[^)]*\)`) @@ -33,7 +33,6 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes compileUnVector := regexp.MustCompile(`Vector<(.*?)>`) compileUnknownVectorType := regexp.MustCompile(`\(\((.*?)\).*get`) dialogResolver := regexp.MustCompile(`DialogObject\..+\(`) - compileWireUsage := regexp.MustCompile(`(outputSerializedData\.write\w*\(this\.\w+\.(\w+)\)|outputSerializedData\.write\w*\([^)]*\bthis\.(\w+)\b|inputSerializedData\.read\w*\([^)]*\)[^;]*\bthis\.(\w+)\s*=|this\.(\w+)\s*=\s*inputSerializedData\.read\w*\([^)]*\)|this\.(\w+)\.serializeToStream\(|this\.(\w+)\s*=\s*[^;]*\.TLdeserialize\(|Vector\.(?:de)?serialize\w*\([^;]*?\bthis\.(\w+)\s*\)\s*;|this\.(\w+)\s*=\s*Vector\.(?:de)?serialize\w*\(|this\.(\w+)\s*=\s*(?:TLObject\.)?hasFlag\(|(?:TLObject\.)?setFlag\([^)]*\bthis\.(\w+)\b)`) for pos, line := range class.Content { if dialogResolver.MatchString(line.Line) { continue @@ -101,6 +100,15 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes forNesting = line.Nesting - 1 } if matches := compileVars.FindAllStringSubmatch(line.Line, -1); len(matches) > 0 { + // Skip matches where the captured field name is a method call (e.g. "serializeToStream(...)") + fieldName := matches[0][2] + if strings.Contains(fieldName, "(") || strings.Contains(fieldName, "$") { + continue + } + // Skip comparison matches ("this.X != ...") unless inside a flag context + if strings.HasPrefix(matches[0][3], " !") && !openedFlags { + continue + } var parameter schemeTypes.Parameter var fromBuffer bool parameter.Name = matches[0][2] @@ -200,64 +208,5 @@ func extractParams(class *javaTypes.RawClass, declarationPos int) ([]schemeTypes break } } - params = append(params, findTwoLevelWireFields(params, class.Content, declarationPos)...) - return filterNonWireParams(params, class.Content, declarationPos, compileWireUsage), nil -} - -func findTwoLevelWireFields(params []schemeTypes.Parameter, content []javaTypes.LineInfo, declarationPos int) []schemeTypes.Parameter { - existing := make(map[string]bool) - for _, p := range params { - existing[p.Name] = true - } - compileTwoLevelWrite := regexp.MustCompile(`outputSerializedData\.write(Int32|Int64|Bool|String|ByteArray|Double)\(this\.\w+\.(\w+)\)`) - writeTypeToTL := map[string]string{ - "Int32": "int", "Int64": "long", "Bool": "Bool", "String": "string", "ByteArray": "bytes", "Double": "double", - } - var found []schemeTypes.Parameter - seen := make(map[string]bool) - for pos, line := range content { - if pos <= declarationPos { - continue - } - if pos > declarationPos && line.Nesting == 1 { - break - } - matches := compileTwoLevelWrite.FindAllStringSubmatch(line.Line, -1) - for _, m := range matches { - name := fixParamName(m[2]) - if existing[name] || seen[name] { - continue - } - seen[name] = true - found = append(found, schemeTypes.Parameter{Name: name, Type: writeTypeToTL[m[1]]}) - } - } - return found -} - -func filterNonWireParams(params []schemeTypes.Parameter, content []javaTypes.LineInfo, declarationPos int, wireUsage *regexp.Regexp) []schemeTypes.Parameter { - confirmed := make(map[string]bool) - for pos, line := range content { - if pos <= declarationPos { - continue - } - if pos > declarationPos && line.Nesting == 1 { - break - } - for _, match := range wireUsage.FindAllStringSubmatch(line.Line, -1) { - for _, name := range match[1:] { - if len(name) > 0 { - confirmed[fixParamName(name)] = true - } - } - } - } - var filtered []schemeTypes.Parameter - flagNameRe := regexp.MustCompile(`^flags[0-9]*$`) - for _, p := range params { - if flagNameRe.MatchString(p.Name) || confirmed[p.Name] { - filtered = append(filtered, p) - } - } - return filtered + return params, nil } diff --git a/utils/merge_parameters.go b/utils/merge_parameters.go index 38af7b6..d09a401 100644 --- a/utils/merge_parameters.go +++ b/utils/merge_parameters.go @@ -25,10 +25,10 @@ func MergeParameters(old, new []types.Parameter, isSameConstructor bool) []types if i < len(old) { content := old[i] res := flagExtractor.FindAllStringSubmatch(content.Type, -1) - if len(res) > 0 && slices.Contains(availableFlags, res[0][1]) && - !slices.Contains(addableKeys, content.Name) && + if !slices.Contains(addableKeys, content.Name) && !slices.Contains(keys, content.Name) && - isSameConstructor { + isSameConstructor && + (len(res) == 0 || slices.Contains(availableFlags, res[0][1])) { mergedList = append(mergedList, content) keys = append(keys, content.Name) }