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

[class-parse] Loosely match parameter names, backwards #900

Merged
merged 1 commit into from
Nov 8, 2021
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
99 changes: 11 additions & 88 deletions src/Xamarin.Android.Tools.Bytecode/Methods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public ParameterInfo[] GetParameters ()
UpdateParametersFromMethodParametersAttribute (parameters);
return parameters;
}

static IEnumerable<string> ExtractTypesFromSignature (string signature)
{
if (signature == null || signature.Length < "()V".Length)
Expand All @@ -106,6 +107,7 @@ static IEnumerable<string> ExtractTypesFromSignature (string signature)
yield return Signature.ExtractType (signature, ref index);
}
}

List<ParameterInfo> GetParametersFromDescriptor (out int endParams)
{
var signature = Descriptor;
Expand Down Expand Up @@ -165,43 +167,18 @@ void UpdateParametersFromLocalVariables (ParameterInfo[] parameters)
return;

var names = locals.LocalVariables.Where (p => p.StartPC == 0).ToList ();
int namesStart = 0;
if (!AccessFlags.HasFlag (MethodAccessFlags.Static) &&
names.Count > namesStart &&
names [namesStart].Descriptor == DeclaringType.FullJniName) {
namesStart++; // skip `this` parameter
}
if (!DeclaringType.IsStatic &&
IsConstructor &&
names.Count > namesStart &&
DeclaringType.InnerClass != null && DeclaringType.InnerClass.OuterClassName != null &&
names [namesStart].Descriptor == "L" + DeclaringType.InnerClass.OuterClassName + ";") {
namesStart++; // "outer `this`", for non-static inner classes
}
if (!DeclaringType.IsStatic &&
IsConstructor &&
names.Count > namesStart &&
DeclaringType.TryGetEnclosingMethodInfo (out var declaringClass, out var _, out var _) &&
names [namesStart].Descriptor == "L" + declaringClass + ";") {
namesStart++; // "outer `this`", for non-static inner classes
}

// For JvmOverloadsConstructor.<init>.(LJvmOverloadsConstructor;IILjava/lang/String;)V
if (namesStart > 0 &&
names.Count > namesStart &&
parameters.Length > 0 &&
names [namesStart].Descriptor != parameters [0].Type.BinaryName &&
names [namesStart-1].Descriptor == parameters [0].Type.BinaryName) {
namesStart--;
}

int parametersCount = GetDeclaredParametersCount (parameters);
CheckDescriptorVariablesToLocalVariables (parameters, parametersCount, names, namesStart);

int max = Math.Min (parametersCount, names.Count - namesStart);
for (int i = 0; i < max; ++i) {
parameters [i].Name = names [namesStart+i].Name;
CheckLocalVariableTypeToDescriptorType (i, parameters, names, namesStart);
for (int pi = parametersCount-1; pi >= 0; --pi) {
for (int ni = names.Count-1; ni >= 0; --ni) {
if (parameters [pi].Type.BinaryName != names [ni].Descriptor) {
continue;
}
parameters [pi].Name = names [ni].Name;
names.RemoveAt (ni);
break;
}
}
}

Expand Down Expand Up @@ -273,60 +250,6 @@ void UpdateParametersFromSignature (ParameterInfo[] parameters)
}
}

void CheckDescriptorVariablesToLocalVariables (ParameterInfo[] parameters, int parametersCount, List<LocalVariableTableEntry> names, int namesStart)
{
if (AccessFlags.HasFlag (MethodAccessFlags.Synthetic))
return;
if ((names.Count - namesStart) == parametersCount)
return;
if (IsEnumCtor)
return;

var paramsDesc = CreateParametersList (parameters, (v, i) => $"`{v.Type.BinaryName}` {v.Name}{(i >= parametersCount ? " /* abi; ignored */" : "")}");
var localsDesc = CreateParametersList (names, (v, i) => $"`{v.Descriptor}` {v.Name}{(i < namesStart ? " /* abi; skipped */" : "")}");

Log.Debug ($"class-parse: method {DeclaringType.ThisClass.Name.Value}.{Name}.{Descriptor}: namesStart={namesStart}; " +
$"Local variables array has {names.Count - namesStart} entries {localsDesc}; " +
$"descriptor has {parametersCount} entries {paramsDesc}!");
}

static string CreateParametersList<T>(IEnumerable<T> values, Func<T, int, string> createElement)
{
var description = new StringBuilder ()
.Append ("(");

int index = 0;
var first = true;
foreach (var v in values) {
if (!first) {
description.Append (", ");
}
first = false;
description.Append (createElement (v, index));
index++;
}
description.Append (")");

return description.ToString ();
}

void CheckLocalVariableTypeToDescriptorType (int index, ParameterInfo[] parameters, List<LocalVariableTableEntry> names, int namesStart)
{
if (AccessFlags.HasFlag (MethodAccessFlags.Synthetic))
return;

var parameterType = parameters [index].Type.BinaryName;
var descriptorType = names [index + namesStart].Descriptor;
if (parameterType == descriptorType)
return;

var paramsDesc = CreateParametersList (parameters, (v, i) => $"`{v.Type.BinaryName}` {v.Name}");
var localsDesc = CreateParametersList (names, (v, i) => $"`{v.Descriptor}` {v.Name}{(i < namesStart ? " /* abi; skipped */" : "")}");

Log.Debug ($"class-parse: method {DeclaringType.ThisClass.Name.Value}.{Name}.{Descriptor}: " +
$"Local variables array {localsDesc} element {index+namesStart} with type `{descriptorType}` doesn't match expected descriptor list {paramsDesc} element {index} with type `{parameterType}`.");
}

void CheckDescriptorVariablesToSignatureParameters (ParameterInfo[] parameters, int parametersCount, MethodTypeSignature sig)
{
if (IsEnumCtor)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;

using Xamarin.Android.Tools.Bytecode;

using NUnit.Framework;

namespace Xamarin.Android.Tools.BytecodeTests {

[TestFixture]
public class JavaTypeNoParametersTests : ClassFileFixture {

const string JavaType = "JavaTypeNoParameters";

[Test]
public void ClassFile_WithNonGenericGlobalType_class ()
{
var c = LoadClassFile (JavaType + ".class");
new ExpectedTypeDeclaration {
MajorVersion = 0x34,
MinorVersion = 0,
ConstantPoolCount = 18,
AccessFlags = ClassAccessFlags.Public | ClassAccessFlags.Super,
FullName = "com/xamarin/JavaTypeNoParameters",
Superclass = new TypeInfo ("java/lang/Object", "Ljava/lang/Object;"),
Methods = {
new ExpectedMethodDeclaration {
Name = "<init>",
AccessFlags = MethodAccessFlags.Public,
ReturnDescriptor = "V",
Parameters = {
new ParameterInfo ("copy", "Lcom/xamarin/JavaTypeNoParameters;", "Lcom/xamarin/JavaTypeNoParameters;"),
},
},
}
}.Assert (c);
}

[Test]
public void XmlDeclaration_WithNonGenericGlobalType_class ()
{
AssertXmlDeclaration (JavaType + ".class", JavaType + ".xml");
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<api
api-source="class-parse">
<package
name="com.xamarin"
jni-name="com/xamarin">
<class
abstract="false"
deprecated="not deprecated"
jni-extends="Ljava/lang/Object;"
extends="java.lang.Object"
extends-generic-aware="java.lang.Object"
final="false"
name="JavaTypeNoParameters"
jni-signature="Lcom/xamarin/JavaTypeNoParameters;"
source-file-name="JavaTypeNoParameters.java"
static="false"
visibility="public">
<constructor
deprecated="not deprecated"
final="false"
name="JavaTypeNoParameters"
static="false"
visibility="public"
bridge="false"
synthetic="false"
jni-signature="(Lcom/xamarin/JavaTypeNoParameters;)V">
<parameter
name="copy"
type="com.xamarin.JavaTypeNoParameters"
jni-type="Lcom/xamarin/JavaTypeNoParameters;" />
</constructor>
</class>
</package>
</api>
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<EmbeddedResource Include="$(IntermediateOutputPath)classes\com\xamarin\JavaType%24RNC%24RPNC.class" />
<EmbeddedResource Include="$(IntermediateOutputPath)classes\com\xamarin\JavaType%24RNC.class" />
<EmbeddedResource Include="$(IntermediateOutputPath)classes\com\xamarin\JavaType.class" />
<EmbeddedResource Include="$(IntermediateOutputPath)classes\com\xamarin\JavaTypeNoParameters.class" />
<EmbeddedResource Include="$(IntermediateOutputPath)classes\com\xamarin\NestedInterface%24DnsSdTxtRecordListener.class" />
<EmbeddedResource Include="$(IntermediateOutputPath)classes\com\xamarin\NestedInterface.class" />
<EmbeddedResource Include="$(IntermediateOutputPath)classes\com\xamarin\ParameterAbstractClass.class" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
<Project>

<ItemGroup>
<TestJar Include="java\**\*.java" Exclude="java\java\util\Collection.java,java\android\annotation\NonNull.java" />
<TestJarNoParameters Include="java\java\util\Collection.java" />
<TestJarNoParameters Include="java/java/util/Collection.java" />
<TestJarNoParameters Include="java/**/*NoParameters.java" />
<TestJar Include="java\**\*.java" Exclude="@(TestJarNoParameters);java\android\annotation\NonNull.java" />
<TestKotlinJar Include="kotlin\**\*.kt" />
</ItemGroup>

<ItemGroup>
<_BuildClassOutputs Include="@(TestJar->'$(IntermediateOutputPath)classes\%(RecursiveDir)%(Filename).class')" />
<_BuildClassOutputs Include="@(TestJarNoParameters->'$(IntermediateOutputPath)classes\%(RecursiveDir)%(Filename).class')" />
</ItemGroup>

<Target Name="BuildClasses"
BeforeTargets="BeforeBuild"
Inputs="@(TestJar)"
Outputs="@(TestJar->'$(IntermediateOutputPath)classes\%(RecursiveDir)%(Filename).class')">
Inputs="@(TestJar);@(TestJarNoParameters)"
Outputs="@(_BuildClassOutputs)">
<MakeDir Directories="$(IntermediateOutputPath)classes" />
<Exec Command="&quot;$(JavaCPath)&quot; -parameters $(_JavacSourceOptions) -g -d &quot;$(IntermediateOutputPath)classes&quot; java/android/annotation/NonNull.java @(TestJar->'%(Identity)', ' ')" />
<Exec Command="&quot;$(JavaCPath)&quot; $(_JavacSourceOptions) -g -d &quot;$(IntermediateOutputPath)classes&quot; @(TestJarNoParameters->'%(Identity)', ' ')" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.xamarin;

public class JavaTypeNoParameters {
/**
* JNI sig: (Lcom/xamarin/JavaTypeNoParameters;)V
*/
public JavaTypeNoParameters (JavaTypeNoParameters copy) {
}
}