Skip to content

Commit

Permalink
improved android support
Browse files Browse the repository at this point in the history
  • Loading branch information
wenshao committed May 28, 2023
1 parent 0bbd0d1 commit 5621a4c
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 35 deletions.
151 changes: 130 additions & 21 deletions core/src/main/java/com/alibaba/fastjson2/internal/asm/ASMUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.*;

Expand Down Expand Up @@ -78,10 +81,55 @@ public class ASMUtils {
public static final String DESC_SUPPLIER = "Ljava/util/function/Supplier;";
public static final String DESC_JSONSCHEMA = 'L' + JSONSchema.class.getName().replace('.', '/') + ';';

static final Map<MethodInfo, String[]> paramMapping = new HashMap<>();

static Map<Class, String> descMapping = new HashMap<>();
static Map<Class, String> typeMapping = new HashMap<>();

static {
paramMapping.put(
new MethodInfo(
"com.alibaba.fastjson2.util.ParameterizedTypeImpl",
"<init>",
new String[]{"[Ljava.lang.reflect.Type;", "java.lang.reflect.Type", "java.lang.reflect.Type"}
),
new String[]{"actualTypeArguments", "ownerType", "rawType"}
);

paramMapping.put(
new MethodInfo(
"org.apache.commons.lang3.tuple.Triple",
"of",
new String[]{"java.lang.Object", "java.lang.Object", "java.lang.Object"}
),
new String[]{"left", "middle", "right"}
);
paramMapping.put(
new MethodInfo(
"org.apache.commons.lang3.tuple.MutableTriple",
"<init>",
new String[]{"java.lang.Object", "java.lang.Object", "java.lang.Object"}
),
new String[]{"left", "middle", "right"}
);

paramMapping.put(
new MethodInfo(
"org.javamoney.moneta.Money",
"<init>",
new String[]{"java.math.BigDecimal", "javax.money.CurrencyUnit", "javax.money.MonetaryContext"}
),
new String[]{"number", "currency", "monetaryContext"}
);
paramMapping.put(
new MethodInfo(
"org.javamoney.moneta.Money",
"<init>",
new String[]{"java.math.BigDecimal", "javax.money.CurrencyUnit"}
),
new String[]{"number", "currency"}
);

descMapping.put(int.class, "I");
descMapping.put(void.class, "V");
descMapping.put(boolean.class, "Z");
Expand Down Expand Up @@ -279,6 +327,11 @@ public static String[] lookupParameterNames(AccessibleObject methodOrCtor) {
return new String[paramCount];
}

String[] paramNames = paramMapping.get(new MethodInfo(declaringClass.getName(), name, types));
if (paramNames != null) {
return paramNames;
}

ClassLoader classLoader = declaringClass.getClassLoader();
if (classLoader == null) {
classLoader = ClassLoader.getSystemClassLoader();
Expand All @@ -288,30 +341,86 @@ public static String[] lookupParameterNames(AccessibleObject methodOrCtor) {
String resourceName = className.replace('.', '/') + ".class";
InputStream is = classLoader.getResourceAsStream(resourceName);

if (is == null) {
return new String[paramCount];
if (is != null) {
try {
ClassReader reader = new ClassReader(is);
TypeCollector visitor = new TypeCollector(name, types);
reader.accept(visitor);

paramNames = visitor.getParameterNamesForMethod();
if (paramNames != null && paramNames.length == paramCount - 1) {
Class<?> dd = declaringClass.getDeclaringClass();
if (dd != null && dd.equals(types[0])) {
String[] strings = new String[paramCount];
strings[0] = "this$0";
System.arraycopy(paramNames, 0, strings, 1, paramNames.length);
paramNames = strings;
}
}
return paramNames;
} catch (IOException ignored) {
// ignored
} finally {
IOUtils.close(is);
}
}

try {
ClassReader reader = new ClassReader(is);
TypeCollector visitor = new TypeCollector(name, types);
reader.accept(visitor);

String[] params = visitor.getParameterNamesForMethod();
if (params != null && params.length == paramCount - 1) {
Class<?> dd = declaringClass.getDeclaringClass();
if (dd != null && dd.equals(types[0])) {
String[] strings = new String[paramCount];
strings[0] = "this$0";
System.arraycopy(params, 0, strings, 1, params.length);
params = strings;
}
paramNames = new String[paramCount];
int i;
if (types[0] == declaringClass.getDeclaringClass()
&& !Modifier.isStatic(declaringClass.getModifiers())) {
paramNames[0] = "this.$0";
i = 1;
} else {
i = 0;
}
for (; i < paramNames.length; i++) {
paramNames[i] = "arg" + i;
}
return paramNames;
}

static class MethodInfo {
final String className;
final String methodName;
final String[] paramTypeNames;
int hash;

public MethodInfo(String className, String methodName, String[] paramTypeNames) {
this.className = className;
this.methodName = methodName;
this.paramTypeNames = paramTypeNames;
}

public MethodInfo(String className, String methodName, Class[] paramTypes) {
this.className = className;
this.methodName = methodName;
this.paramTypeNames = new String[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
paramTypeNames[i] = paramTypes[i].getName();
}
return params;
} catch (IOException e) {
return new String[paramCount];
} finally {
IOUtils.close(is);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MethodInfo that = (MethodInfo) o;
return Objects.equals(className, that.className) && Objects.equals(methodName, that.methodName) && Arrays.equals(paramTypeNames, that.paramTypeNames);
}

@Override
public int hashCode() {
if (hash == 0) {
int result = Objects.hash(className, methodName);
result = 31 * result + Arrays.hashCode(paramTypeNames);
hash = result;
}
return hash;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.alibaba.fastjson2.reader;

import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONFactory;
import com.alibaba.fastjson2.codec.FieldInfo;
import com.alibaba.fastjson2.internal.asm.ASMUtils;
import com.alibaba.fastjson2.util.Fnv;
import com.alibaba.fastjson2.util.TypeUtils;
Expand Down Expand Up @@ -68,6 +70,20 @@ final class ConstructorFunction<T>
alternateConstructor.setAccessible(true);

String[] parameterNames = ASMUtils.lookupParameterNames(alternateConstructor);

Parameter[] parameters = alternateConstructor.getParameters();
FieldInfo fieldInfo = new FieldInfo();
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
for (int i = 0; i < parameters.length && i < parameterNames.length; i++) {
fieldInfo.init();

Parameter parameter = parameters[i];
provider.getFieldInfo(fieldInfo, alternateConstructor.getDeclaringClass(), alternateConstructor, i, parameter);
if (fieldInfo.fieldName != null) {
parameterNames[i] = fieldInfo.fieldName;
}
}

long[] parameterNameHashCodes = new long[parameterNames.length];
Type[] parameterTypes = alternateConstructor.getGenericParameterTypes();
Set<Long> paramHashCodes = new HashSet<>(parameterNames.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public void readFieldValue(JSONReader jsonReader, T object) {
schema.assertValidate(fieldValue);
}

if (fieldValue == null && defaultValue != null) {
return;
}

try {
field.set(object, fieldValue);
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ public void readFieldValue(JSONReader jsonReader, T object) {
}
}

if (fieldValue == null && defaultValue != null) {
return;
}

if (schema != null) {
schema.assertValidate(fieldValue);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public void readFieldValue(JSONReader jsonReader, T object) {
schema.assertValidate(fieldValue);
}

if (fieldValue == null && defaultValue != null) {
return;
}

try {
method.invoke(object, fieldValue);
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -668,14 +668,28 @@ public void getFieldInfo(
}
}

boolean staticClass = Modifier.isStatic(constructor.getDeclaringClass().getModifiers());
Annotation[] annotations = null;
try {
annotations = getAnnotations(parameter);
} catch (ArrayIndexOutOfBoundsException ignored) {
// ignored
if (staticClass) {
try {
annotations = getAnnotations(parameter);
} catch (ArrayIndexOutOfBoundsException ignored) {
// ignored
}
} else {
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
int paIndex;
if (parameterAnnotations.length == constructor.getParameterCount()) {
paIndex = paramIndex;
} else {
paIndex = paramIndex - 1;
}
if (paIndex >= 0 && paIndex < parameterAnnotations.length) {
annotations = parameterAnnotations[paIndex];
}
}

if (annotations != null) {
if (annotations != null && annotations.length > 0) {
processAnnotation(fieldInfo, annotations);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,19 @@ boolean record = BeanUtils.isRecord(objectClass);

if (parameterNames == null || parameterNames.length == 0) {
parameterNames = ASMUtils.lookupParameterNames(creatorConstructor);

Parameter[] parameters = creatorConstructor.getParameters();
FieldInfo fieldInfo = new FieldInfo();
for (int i = 0; i < parameters.length && i < parameterNames.length; i++) {
fieldInfo.init();

Parameter parameter = parameters[i];

provider.getFieldInfo(fieldInfo, objectClass, creatorConstructor, i, parameter);
if (fieldInfo.fieldName != null) {
parameterNames[i] = fieldInfo.fieldName;
}
}
}

int matchCount = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package com.alibaba.fastjson2.reader;

import com.alibaba.fastjson2.JSONB;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONPath;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.*;
import com.alibaba.fastjson2.codec.FieldInfo;
import com.alibaba.fastjson2.internal.asm.ASMUtils;
import com.alibaba.fastjson2.util.BeanUtils;
import com.alibaba.fastjson2.util.Fnv;

import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.time.format.DateTimeParseException;
import java.util.*;
Expand Down Expand Up @@ -104,7 +103,22 @@ protected ObjectReaderException(
String[] parameterNames = null;
if (paramCount > 0) {
parameterNames = ASMUtils.lookupParameterNames(constructor);

Parameter[] parameters = constructor.getParameters();
FieldInfo fieldInfo = new FieldInfo();
for (int i = 0; i < parameters.length && i < parameterNames.length; i++) {
fieldInfo.init();

Parameter parameter = parameters[i];

ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
provider.getFieldInfo(fieldInfo, objectClass, constructor, i, parameter);
if (fieldInfo.fieldName != null) {
parameterNames[i] = fieldInfo.fieldName;
}
}
}

constructorParameters.add(parameterNames);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.util.GuavaSupport;

import java.lang.reflect.Type;
import java.util.*;
Expand Down Expand Up @@ -128,10 +129,29 @@ public Object readJSONBObject(JSONReader jsonReader, Type fieldType, Object fiel
list = new ArrayList();
builder = (Function<Collection, Collection>) ((Collection collection) -> Collections.singletonList(collection.iterator().next()));
} else if (instanceType != null && instanceType != this.listType) {
try {
list = (Collection) instanceType.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new JSONException(jsonReader.info("create instance error " + instanceType), e);
String typeName = instanceType.getTypeName();
switch (typeName) {
case "com.google.common.collect.ImmutableList":
list = new ArrayList();
builder = GuavaSupport.immutableListConverter();
break;
case "com.google.common.collect.ImmutableSet":
list = new ArrayList();
builder = GuavaSupport.immutableSetConverter();
break;
case "com.google.common.collect.Lists$TransformingRandomAccessList":
list = new ArrayList();
break;
case "com.google.common.collect.Lists.TransformingSequentialList":
list = new LinkedList();
break;
default:
try {
list = (Collection) instanceType.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new JSONException(jsonReader.info("create instance error " + instanceType), e);
}
break;
}
} else {
list = (Collection) createInstance(jsonReader.getContext().getFeatures() | features);
Expand Down
Loading

0 comments on commit 5621a4c

Please sign in to comment.