Speed up SkipValue (#3365)

* add simd-benchmark

* checkstyle

* add simdjson benchmark

* fix checkstyle

* fix benchmark

* fix benchmark

* fix benchmark

* fast skip

* bug fix

* fieldClassSerializable for Record

* fast skip

* check style

* simplify skipNumber

* update benchmark

* update benchmark

* update benchmark

* optimize skipValue

* optimize skipValue

* optimize skipValue
This commit is contained in:
Shaojin Wen 2025-03-07 17:36:38 +08:00 committed by GitHub
parent 1e9204fa00
commit c17a8d4a0d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 2132 additions and 546 deletions

View File

@ -0,0 +1,26 @@
package com.alibaba.fastjson2.benchmark.simdjson;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.benchmark.eishay.EishayParseBinary;
import org.apache.commons.io.IOUtils;
import org.openjdk.jmh.infra.Blackhole;
import java.io.IOException;
import java.io.InputStream;
public class GithubEvents {
static final String file = "data/simd-json/github_events.json";
static byte[] bytes;
static {
try (InputStream is = EishayParseBinary.class.getClassLoader().getResourceAsStream(file)) {
String str = IOUtils.toString(is, "UTF-8");
bytes = str.getBytes();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void fastjson2_parse(Blackhole bh) {
bh.consume(JSON.parseArray(bytes));
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,22 @@
package com.alibaba.fastjson2.benchmark.simdjson;
import static com.alibaba.fastjson2.benchmark.JMH.BH;
public class GithubEventsTest {
private static final GithubEvents benchmark = new GithubEvents();
public static void fastjson2_parse() {
for (int j = 0; j < 5; j++) {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000 * 10; ++i) {
benchmark.fastjson2_parse(BH);
}
long millis = System.currentTimeMillis() - start;
System.out.println("GithubEvents-fastjson2_parse : " + millis);
}
}
public static void main(String[] args) throws Exception {
fastjson2_parse();
}
}

215
benchmark_21/pom.xml Normal file
View File

@ -0,0 +1,215 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-parent</artifactId>
<version>2.0.57-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>fastjson2-benchmark_21</artifactId>
<name>fastjson2-benchmark_21</name>
<description>JMH Benchmark 21 for Fastjson2</description>
<packaging>jar</packaging>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
<maven.test.skip>true</maven.test.skip>
<uberjar.name>fastjson2-benchmarks</uberjar.name>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson1x.version}</version>
</dependency>
<!--
<dependency>
<groupId>com.cainiao.ai</groupId>
<artifactId>seq-csv</artifactId>
<version>0.1.3</version>
</dependency>
-->
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
</dependency>
<dependency>
<groupId>com.dslplatform</groupId>
<artifactId>dsl-json</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.6.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.github.erosb</groupId>
<artifactId>everit-json-schema</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
</dependency>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
</dependency>
<dependency>
<groupId>com.univocity</groupId>
<artifactId>univocity-parsers</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>io.github.wycst</groupId>
<artifactId>wast</artifactId>
<version>0.0.25</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>net.sourceforge.javacsv</groupId>
<artifactId>javacsv</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.arrow</groupId>
<artifactId>arrow-vector</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.fury</groupId>
<artifactId>fury-core</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>jackson-dataformat-msgpack</artifactId>
<version>0.9.9</version>
</dependency>
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>msgpack-core</artifactId>
<version>0.9.8</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.7.1</version>
</dependency>
<dependency>
<groupId>org.simdjson</groupId>
<artifactId>simdjson-java</artifactId>
<version>0.3.0</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>${uberjar.name}</finalName>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<filters>
<filter>
<!--
Shading signed JARs will fail without this.
http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
-->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,79 @@
package com.alibaba.fastjson2.benchmark.simdjson;
import com.alibaba.fastjson2.JSON;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.simdjson.JsonValue;
import org.simdjson.SimdJsonParser;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import static com.alibaba.fastjson2.benchmark.simdjson.SimdJsonPaddingUtil.padded;
@State(Scope.Benchmark)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public class ParseBenchmark {
@Param({"/data/simd-json/twitter.json", "/data/simd-json/gsoc-2018.json", "/data/simd-json/github_events.json"})
String fileName;
private final SimdJsonParser simdJsonParser = new SimdJsonParser();
private byte[] buffer;
private byte[] bufferPadded;
@Setup(Level.Trial)
public void setup() throws IOException {
try (InputStream is = ParseBenchmark.class.getResourceAsStream(fileName)) {
byte[] buf = new byte[1024 * 1024 * 64];
int count = is.read(buf);
buffer = Arrays.copyOf(buf, count);
bufferPadded = padded(buffer);
}
}
@Benchmark
public JsonValue simdjson() {
return simdJsonParser.parse(buffer, buffer.length);
}
@Benchmark
public Object fastjson() {
return JSON.parse(buffer);
}
@Benchmark
public Object wast() {
return io.github.wycst.wast.json.JSON.parse(buffer);
}
@Benchmark
public JsonValue simdjsonPadded() {
return simdJsonParser.parse(bufferPadded, buffer.length);
}
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(ParseBenchmark.class.getName())
.jvmArgsAppend("--add-opens=java.base/java.time=ALL-UNNAMED", "--add-modules=jdk.incubator.vector")
.mode(Mode.Throughput)
.timeUnit(TimeUnit.MILLISECONDS)
.forks(1)
.build();
new Runner(options).run();
}
}

View File

@ -0,0 +1,142 @@
package com.alibaba.fastjson2.benchmark.simdjson;
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.simdjson.SimdJsonParser;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static com.alibaba.fastjson2.benchmark.simdjson.SimdJsonPaddingUtil.padded;
@State(Scope.Benchmark)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public class SchemaBasedParseAndSelectBenchmark {
private final SimdJsonParser simdJsonParser;
private final ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
public SchemaBasedParseAndSelectBenchmark() {
SimdJsonParser p = null;
try {
p = new SimdJsonParser();
} catch (Throwable e) {
e.printStackTrace();
}
simdJsonParser = p;
}
private byte[] buffer;
private byte[] bufferPadded;
@Setup(Level.Trial)
public void setup() throws IOException {
try (InputStream is = ParseBenchmark.class.getResourceAsStream("/data/simd-json/twitter.json")) {
buffer = is.readAllBytes();
bufferPadded = padded(buffer);
}
}
@Benchmark
public int countUniqueUsersWithDefaultProfile_simdjson() {
Set<String> defaultUsers = new HashSet<>();
SimdJsonTwitter twitter = simdJsonParser.parse(buffer, buffer.length, SimdJsonTwitter.class);
for (SimdJsonStatus status : twitter.statuses()) {
SimdJsonUser user = status.user();
if (user.default_profile()) {
defaultUsers.add(user.screen_name());
}
}
return defaultUsers.size();
}
@Benchmark
public int countUniqueUsersWithDefaultProfile_simdjsonPadded() {
Set<String> defaultUsers = new HashSet<>();
SimdJsonTwitter twitter = simdJsonParser.parse(bufferPadded, buffer.length, SimdJsonTwitter.class);
for (SimdJsonStatus status : twitter.statuses()) {
SimdJsonUser user = status.user();
if (user.default_profile()) {
defaultUsers.add(user.screen_name());
}
}
return defaultUsers.size();
}
@Benchmark
public int countUniqueUsersWithDefaultProfile_jackson() throws IOException {
Set<String> defaultUsers = new HashSet<>();
SimdJsonTwitter twitter = objectMapper.readValue(buffer, SimdJsonTwitter.class);
for (SimdJsonStatus status : twitter.statuses()) {
SimdJsonUser user = status.user();
if (user.default_profile()) {
defaultUsers.add(user.screen_name());
}
}
return defaultUsers.size();
}
@Benchmark
public int countUniqueUsersWithDefaultProfile_fastjson() {
Set<String> defaultUsers = new HashSet<>();
SimdJsonTwitter twitter = JSON.parseObject(buffer, SimdJsonTwitter.class);
for (SimdJsonStatus status : twitter.statuses()) {
SimdJsonUser user = status.user();
if (user.default_profile()) {
defaultUsers.add(user.screen_name());
}
}
return defaultUsers.size();
}
@Benchmark
public int countUniqueUsersWithDefaultProfile_wast() {
Set<String> defaultUsers = new HashSet<>();
SimdJsonTwitter twitter = io.github.wycst.wast.json.JSON.parseObject(buffer, SimdJsonTwitter.class);
for (SimdJsonStatus status : twitter.statuses()) {
SimdJsonUser user = status.user();
if (user.default_profile()) {
defaultUsers.add(user.screen_name());
}
}
return defaultUsers.size();
}
public record SimdJsonUser(boolean default_profile, String screen_name) {
}
public record SimdJsonStatus(SimdJsonUser user) {
}
public record SimdJsonTwitter(List<SimdJsonStatus> statuses) {
}
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(SchemaBasedParseAndSelectBenchmark.class.getName())
.jvmArgsAppend("--add-opens=java.base/java.time=ALL-UNNAMED", "--add-modules=jdk.incubator.vector")
.mode(Mode.Throughput)
.timeUnit(TimeUnit.MILLISECONDS)
.forks(1)
.build();
new Runner(options).run();
}
}

View File

@ -0,0 +1,21 @@
package com.alibaba.fastjson2.benchmark.simdjson;
import java.util.Arrays;
import static java.nio.charset.StandardCharsets.UTF_8;
class SimdJsonPaddingUtil {
static byte[] padded(byte[] src) {
byte[] bufferPadded = new byte[src.length + 64];
System.arraycopy(src, 0, bufferPadded, 0, src.length);
return bufferPadded;
}
static byte[] padWithSpaces(String str) {
byte[] strBytes = str.getBytes(UTF_8);
byte[] padded = new byte[strBytes.length + 64];
Arrays.fill(padded, (byte) ' ');
System.arraycopy(strBytes, 0, padded, 0, strBytes.length);
return padded;
}
}

View File

@ -0,0 +1,39 @@
package com.alibaba.fastjson2.benchmark.simdjson;
import com.alibaba.fastjson2.JSON;
import org.apache.commons.io.IOUtils;
import org.openjdk.jmh.infra.Blackhole;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class Twitter {
static final String file = "data/simd-json/twitter.json";
static byte[] bytes;
static {
try (InputStream is = Twitter.class.getClassLoader().getResourceAsStream(file)) {
String str = IOUtils.toString(is, "UTF-8");
bytes = str.getBytes();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void fastjson2_parse(Blackhole bh) {
bh.consume(JSON.parseObject(bytes, SimdJsonTwitter.class));
}
public void wast_parse(Blackhole bh) {
bh.consume(io.github.wycst.wast.json.JSON.parseObject(bytes, SimdJsonTwitter.class));
}
public record SimdJsonUser(boolean default_profile, String screen_name) {
}
public record SimdJsonStatus(SimdJsonUser user) {
}
public record SimdJsonTwitter(List<SimdJsonStatus> statuses) {
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,23 @@
package com.alibaba.fastjson2.benchmark;
import com.alibaba.fastjson2.JSON;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.net.URL;
public class Gsoc2018Test {
@Test
public void test() throws Exception {
String[] paths = new String[] {
"data/simd-json/twitter.json", "data/simd-json/gsoc-2018.json", "data/simd-json/github_events.json"
};
for (String path : paths) {
URL resource = Thread.currentThread().getContextClassLoader().getResource(path);
File file = new File(resource.getFile());
byte[] str = FileUtils.readFileToByteArray(file);
JSON.parse(str);
}
}
}

View File

@ -0,0 +1,7 @@
package com.alibaba.fastjson2.benchmark;
import org.openjdk.jmh.infra.Blackhole;
public class JMH {
public static final Blackhole BH = new Blackhole("Today's password is swordfish. I understand instantiating Blackholes directly is dangerous.");
}

View File

@ -0,0 +1,38 @@
package com.alibaba.fastjson2.benchmark;
import com.alibaba.fastjson2.benchmark.simdjson.Twitter;
import static com.alibaba.fastjson2.benchmark.JMH.BH;
public class TwitterTest {
private static final Twitter benchmark = new Twitter();
public static void fastjson2_parse() {
for (int j = 0; j < 5; j++) {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; ++i) {
benchmark.fastjson2_parse(BH);
}
long millis = System.currentTimeMillis() - start;
System.out.println("Twitter-fastjson2_parse : " + millis);
// zulu21.32.17 247 234
}
}
public static void wast_parse() {
for (int j = 0; j < 5; j++) {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; ++i) {
benchmark.wast_parse(BH);
}
long millis = System.currentTimeMillis() - start;
System.out.println("Twitter-wast_parse : " + millis);
// zulu21.32.17 211
}
}
public static void main(String[] args) throws Exception {
fastjson2_parse();
// wast_parse();
}
}

View File

@ -446,6 +446,10 @@ public abstract class JSONReader
return (context.features & MASK_IGNORE_NONE_SERIALIZABLE) != 0;
}
public boolean hasAutoTypeBeforeHandler() {
return context.autoTypeBeforeHandler != null;
}
public ObjectReader checkAutoType(Class expectClass, long expectClassHash, long features) {
return null;
}
@ -4324,7 +4328,9 @@ public abstract class JSONReader
protected static final long MASK_SUPPORT_AUTO_TYPE = 1L << 5;
protected static final long MASK_SUPPORT_SMART_MATCH = 1L << 6;
protected static final long MASK_TRIM_STRING = 1L << 14;
protected static final long MASK_ALLOW_UN_QUOTED_FIELD_NAMES = 1L << 17;
protected static final long MASK_EMPTY_STRING_AS_NULL = 1L << 27;
protected static final long MASK_DISABLE_SINGLE_QUOTE = 1L << 31L;
protected static final long MASK_DISABLE_REFERENCE_DETECT = 1L << 33;
public enum Feature {
@ -4352,7 +4358,7 @@ public abstract class JSONReader
TrimString(MASK_TRIM_STRING),
ErrorOnNotSupportAutoType(1 << 15),
DuplicateKeyValueAsArray(1 << 16),
AllowUnQuotedFieldNames(1 << 17),
AllowUnQuotedFieldNames(MASK_ALLOW_UN_QUOTED_FIELD_NAMES),
NonStringKeyAsString(1 << 18),
/**
* @since 2.0.13
@ -4449,7 +4455,7 @@ public abstract class JSONReader
* Feature that disables the support for single quote.
* @since 2.0.53
*/
DisableSingleQuote(1L << 31L),
DisableSingleQuote(MASK_DISABLE_SINGLE_QUOTE),
/**
* @since 2.0.53
@ -4518,6 +4524,21 @@ public abstract class JSONReader
this.ch = (char) savePoint.current;
}
final boolean checkNameBegin(int quote) {
long features = context.features;
if (quote == '\'' && ((features & MASK_DISABLE_SINGLE_QUOTE) != 0)) {
throw notSupportName();
}
if (quote != '"' && quote != '\'') {
if ((features & MASK_ALLOW_UN_QUOTED_FIELD_NAMES) != 0) {
readFieldNameHashCodeUnquote();
return true;
}
throw notSupportName();
}
return false;
}
final JSONException notSupportName() {
return new JSONException(info("not support unquoted name"));
}
@ -4534,6 +4555,10 @@ public abstract class JSONReader
return new JSONException(info(message), cause);
}
final JSONException error() {
throw new JSONValidException("error, offset " + offset + ", char " + (char) ch);
}
final JSONException error(int offset, int ch) {
throw new JSONValidException("error, offset " + offset + ", char " + (char) ch);
}

View File

@ -17,8 +17,6 @@ import static com.alibaba.fastjson2.util.JDKUtils.*;
final class JSONReaderASCII
extends JSONReaderUTF8 {
final String str;
static final int ESCAPE_INDEX_NOT_SET = -2;
private int nextEscapeIndex = ESCAPE_INDEX_NOT_SET;
JSONReaderASCII(Context ctx, String str, byte[] bytes, int offset, int length) {
super(ctx, bytes, offset, length);
@ -1359,7 +1357,7 @@ final class JSONReaderASCII
if (index == -1) {
throw error("invalid escape character EOI");
}
int slashIndex = indexOfSlash(bytes, offset, end);
int slashIndex = indexOfSlash(this, bytes, offset, end);
if (slashIndex == -1 || slashIndex > index) {
offset = index + 1;
} else {
@ -1400,14 +1398,6 @@ final class JSONReaderASCII
return readStringNotMatch();
}
private int indexOfSlash(byte[] bytes, int offset, int end) {
int slashIndex = nextEscapeIndex;
if (slashIndex == ESCAPE_INDEX_NOT_SET || (slashIndex != -1 && slashIndex < offset)) {
nextEscapeIndex = slashIndex = IOUtils.indexOfSlash(bytes, offset, end);
}
return slashIndex;
}
@Override
protected final void readString0() {
final byte[] bytes = this.bytes;

View File

@ -2353,56 +2353,6 @@ final class JSONReaderUTF16
return getFieldName();
}
@Override
public final boolean skipName() {
char quote = ch;
if (quote == '\'' && ((context.features & Feature.DisableSingleQuote.mask) != 0)) {
throw notSupportName();
}
if (quote != '"' && quote != '\'') {
if ((context.features & Feature.AllowUnQuotedFieldNames.mask) != 0) {
readFieldNameHashCodeUnquote();
return true;
}
throw notSupportName();
}
int offset = this.offset;
final char[] chars = this.chars;
for (; ; ) {
char ch = chars[offset++];
if (ch == '\\') {
ch = chars[offset];
offset += (ch == 'u' ? 5 : (ch == 'x' ? 3 : 1));
continue;
}
if (ch == quote) {
ch = offset == end ? EOI : chars[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : chars[offset++];
}
if (ch != ':') {
throw syntaxError(ch);
}
ch = offset == end ? EOI : chars[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : chars[offset++];
}
this.offset = offset;
this.ch = ch;
break;
}
}
return true;
}
@Override
public final int readInt32Value() {
char ch = this.ch;
@ -3163,207 +3113,440 @@ final class JSONReaderUTF16
}
@Override
public final void skipValue() {
final char[] chars = this.chars;
char ch = this.ch;
int offset = this.offset, end = this.end;
comma = false;
public final boolean skipName() {
this.offset = skipName(this, chars, offset, end);
return true;
}
switch_:
switch (ch) {
case '-':
case '+':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
boolean sign = ch == '-' || ch == '+';
if (sign) {
private static int skipName(JSONReaderUTF16 jsonReader, char[] chars, int offset, int end) {
char quote = jsonReader.ch;
if (jsonReader.checkNameBegin(quote)) {
return jsonReader.offset;
}
for (; ; ) {
char ch = chars[offset++];
if (ch == '\\') {
ch = chars[offset];
offset += (ch == 'u' ? 5 : (ch == 'x' ? 3 : 1));
continue;
}
if (ch == quote) {
ch = offset == end ? EOI : chars[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : chars[offset++];
}
if (ch != ':') {
throw syntaxError(ch);
}
ch = offset == end ? EOI : chars[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : chars[offset++];
}
jsonReader.ch = ch;
return offset;
}
}
}
private static int skipNumber(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
int ch = jsonReader.ch;
if (ch == '-' || ch == '+') {
if (offset < end) {
ch = bytes[offset++];
} else {
throw jsonReader.error();
}
}
boolean dot = ch == '.';
boolean num = false;
if (!dot && (ch >= '0' && ch <= '9')) {
num = true;
do {
ch = offset == end ? EOI : bytes[offset++];
} while (ch >= '0' && ch <= '9');
}
if (num && (ch == 'L' | ch == 'F' | ch == 'D' | ch == 'B' | ch == 'S')) {
ch = bytes[offset++];
} else {
boolean small = false;
if (ch == '.') {
small = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch >= '0' && ch <= '9') {
ch = offset == end ? EOI : bytes[offset++];
}
}
if (!num && !small) {
throw numberError(offset, ch);
}
if (ch == 'e' || ch == 'E') {
ch = bytes[offset++];
boolean eSign = false;
if (ch == '+' || ch == '-') {
eSign = true;
if (offset < end) {
ch = chars[offset++];
ch = bytes[offset++];
} else {
throw numberError(offset, ch);
}
}
boolean dot = ch == '.';
boolean num = false;
if (!dot && (ch >= '0' && ch <= '9')) {
num = true;
if (ch >= '0' && ch <= '9') {
do {
ch = offset == end ? EOI : chars[offset++];
ch = offset == end ? EOI : bytes[offset++];
} while (ch >= '0' && ch <= '9');
}
if (num && (ch == 'L' || ch == 'F' || ch == 'D' || ch == 'B' || ch == 'S')) {
ch = chars[offset++];
}
boolean small = false;
if (ch == '.') {
small = true;
ch = offset == end ? EOI : chars[offset++];
if (ch >= '0' && ch <= '9') {
do {
ch = offset == end ? EOI : chars[offset++];
} while (ch >= '0' && ch <= '9');
}
}
if (!num && !small) {
} else if (eSign) {
throw numberError(offset, ch);
}
}
if (ch == 'e' || ch == 'E') {
ch = chars[offset++];
if (ch == 'F' || ch == 'D') {
ch = offset == end ? EOI : bytes[offset++];
}
}
boolean eSign = false;
if (ch == '+' || ch == '-') {
eSign = true;
if (offset < end) {
ch = chars[offset++];
} else {
throw numberError(offset, ch);
}
}
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if (ch >= '0' && ch <= '9') {
do {
ch = offset == end ? EOI : chars[offset++];
} while (ch >= '0' && ch <= '9');
} else if (eSign) {
throw numberError(offset, ch);
}
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if (ch == 'L' || ch == 'F' || ch == 'D' || ch == 'B' || ch == 'S') {
ch = offset == end ? EOI : chars[offset++];
}
break;
case 't':
if (offset + 3 > end) {
throw error(offset, ch);
}
if (chars[offset] != 'r' || chars[offset + 1] != 'u' || chars[offset + 2] != 'e') {
throw error(offset, ch);
}
offset += 3;
ch = offset == end ? EOI : chars[offset++];
break;
case 'f':
if (offset + 4 > end) {
throw error(offset, ch);
}
if (chars[offset] != 'a' || chars[offset + 1] != 'l' || chars[offset + 2] != 's' || chars[offset + 3] != 'e') {
throw error(offset, ch);
}
offset += 4;
ch = offset == end ? EOI : chars[offset++];
break;
case 'n':
if (offset + 3 > end) {
throw error(offset, ch);
}
if (chars[offset] != 'u' || chars[offset + 1] != 'l' || chars[offset + 2] != 'l') {
throw error(offset, ch);
}
offset += 3;
ch = offset == end ? EOI : chars[offset++];
break;
case '"':
case '\'': {
char quote = ch;
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipString(JSONReaderUTF16 jsonReader, char[] chars, int offset, int end) {
char quote = jsonReader.ch;
char ch = offset == end ? EOI : chars[offset++];
for (; ; ) {
if (ch == '\\') {
ch = chars[offset++];
for (; ; ) {
if (ch == '\\') {
ch = chars[offset++];
if (ch == 'u') {
offset += 4;
} else if (ch == 'x') {
offset += 2;
} else if (ch != '\\' && ch != '"') {
char1(ch);
}
ch = chars[offset++];
continue;
}
if (ch == quote) {
ch = offset == end ? EOI : chars[offset++];
break;
}
ch = chars[offset++];
if (ch == 'u') {
offset += 4;
} else if (ch == 'x') {
offset += 2;
} else if (ch != '\\' && ch != '"') {
jsonReader.char1(ch);
}
ch = chars[offset++];
continue;
}
if (ch == quote) {
ch = offset == end ? EOI : chars[offset++];
break;
}
default:
if (ch == '[') {
next();
for (int i = 0; ; ++i) {
if (this.ch == ']') {
comma = false;
offset = this.offset;
ch = offset == end ? EOI : chars[offset++];
break switch_;
}
if (i != 0 && !comma) {
throw valueError();
}
comma = false;
skipValue();
}
} else if (ch == '{') {
next();
for (; ; ) {
if (this.ch == '}') {
comma = false;
offset = this.offset;
ch = offset == end ? EOI : chars[offset++];
break switch_;
}
skipName();
skipValue();
}
} else if (ch == 'S' && nextIfSet()) {
skipValue();
} else {
throw error(offset, ch);
}
ch = this.ch;
offset = this.offset;
break;
ch = chars[offset++];
}
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : chars[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : chars[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : chars[offset++];
}
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipStringEscaped(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int quote) {
int ch = bytes[offset++];
for (; ; ) {
if (ch == '\\') {
ch = bytes[offset++];
if (ch == 'u') {
offset += 4;
} else if (ch == 'x') {
offset += 2;
} else if (ch != '\\' && ch != '"') {
jsonReader.char1(ch);
}
ch = bytes[offset++];
continue;
}
if (ch == quote) {
return offset;
}
ch = bytes[offset++];
}
}
private static int skipObject(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
offset = next(jsonReader, bytes, offset, end);
for (int i = 0; ; ++i) {
if (jsonReader.ch == '}') {
break;
}
if (i != 0 && !jsonReader.comma) {
throw jsonReader.valueError();
}
offset = skipName(jsonReader, bytes, offset, end);
offset = skipValue(jsonReader, bytes, offset, end);
}
int ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
public static int next(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
int ch = offset >= end ? EOI : bytes[offset++];
while (ch == '\0' || (ch <= ' ' && ((1L << ch) & SPACE) != 0)) {
ch = offset == end ? EOI : bytes[offset++];
}
jsonReader.ch = (char) ch;
if (ch == '/') {
jsonReader.offset = offset;
jsonReader.skipComment();
return jsonReader.offset;
} else {
return offset;
}
}
private static int skipArray(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
offset = next(jsonReader, bytes, offset, end);
for (int i = 0; ; ++i) {
if (jsonReader.ch == ']') {
break;
}
if (i != 0 && !jsonReader.comma) {
throw jsonReader.valueError();
}
offset = skipValue(jsonReader, bytes, offset, end);
}
int ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
}
if (!comma && ch != '}' && ch != ']' && ch != EOI) {
throw error(offset, ch);
throw jsonReader.error(offset, ch);
}
if (comma && (ch == '}' || ch == ']' || ch == EOI)) {
throw error(offset, ch);
throw jsonReader.error(offset, ch);
}
this.ch = ch;
this.offset = offset;
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipFalse(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
if (offset + 4 > end || IOUtils.notALSE(bytes, offset)) {
throw jsonReader.error();
}
offset += 4;
int ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipTrue(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
if (offset + 3 > end || IOUtils.notTRUE(bytes, offset - 1)) {
throw jsonReader.error();
}
offset += 3;
int ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipNull(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
if (offset + 3 > end || IOUtils.notNULL(bytes, offset - 1)) {
throw jsonReader.error();
}
offset += 3;
int ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipSet(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
if (nextIfSet(jsonReader, bytes, offset, end)) {
return skipArray(jsonReader, bytes, jsonReader.offset, end);
} else {
throw jsonReader.error();
}
}
private static boolean nextIfSet(JSONReaderUTF16 jsonReader, char[] chars, int offset, int end) {
if (offset + 1 < end && chars[offset] == 'e' && chars[offset + 1] == 't') {
offset += 2;
char ch = offset == end ? EOI : chars[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : chars[offset++];
}
jsonReader.offset = offset;
jsonReader.ch = ch;
return true;
}
return false;
}
private static int skipValue(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
switch (jsonReader.ch) {
case 't':
return skipTrue(jsonReader, bytes, offset, end);
case 'f':
return skipFalse(jsonReader, bytes, offset, end);
case 'n':
return skipNull(jsonReader, bytes, offset, end);
case '"':
case '\'':
return skipString(jsonReader, bytes, offset, end);
case '{':
return skipObject(jsonReader, bytes, offset, end);
case '[':
return skipArray(jsonReader, bytes, offset, end);
case 'S':
return skipSet(jsonReader, bytes, offset, end);
default:
return skipNumber(jsonReader, bytes, offset, end);
}
}
@Override
public final void skipValue() {
this.offset = skipValue(this, chars, offset, end);
}
@Override

View File

@ -22,6 +22,8 @@ import static java.nio.charset.StandardCharsets.UTF_8;
class JSONReaderUTF8
extends JSONReader {
static final int REF = BIG_ENDIAN ? 0x24726566 : 0x66657224;
static final int ESCAPE_INDEX_NOT_SET = -2;
protected int nextEscapeIndex = ESCAPE_INDEX_NOT_SET;
protected final byte[] bytes;
protected final int length;
@ -119,6 +121,14 @@ class JSONReaderUTF8
next();
}
static int indexOfSlash(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
int slashIndex = jsonReader.nextEscapeIndex;
if (slashIndex == ESCAPE_INDEX_NOT_SET || (slashIndex != -1 && slashIndex < offset)) {
jsonReader.nextEscapeIndex = slashIndex = IOUtils.indexOfSlash(bytes, offset, end);
}
return slashIndex;
}
private void char_utf8(int ch, int offset) {
final byte[] bytes = this.bytes;
switch ((ch & 0xFF) >> 4) {
@ -4246,236 +4256,276 @@ class JSONReaderUTF8
@Override
public final boolean skipName() {
char quote = ch;
if (quote == '\'' && ((context.features & Feature.DisableSingleQuote.mask) != 0)) {
throw notSupportName();
}
if (quote != '"' && quote != '\'') {
if ((context.features & Feature.AllowUnQuotedFieldNames.mask) != 0) {
readFieldNameHashCodeUnquote();
return true;
}
throw notSupportName();
}
int offset = this.offset;
final byte[] bytes = this.bytes;
for (; ; ) {
int ch = bytes[offset++];
if (ch == '\\') {
ch = bytes[offset];
offset += (ch == 'u' ? 5 : (ch == 'x' ? 3 : 1));
continue;
}
if (ch == quote) {
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if (ch != ':') {
throw syntaxError(ch);
}
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
this.offset = offset;
this.ch = (char) ch;
break;
}
}
this.offset = skipName(this, bytes, offset, end);
return true;
}
@Override
public final void skipValue() {
final byte[] bytes = this.bytes;
int ch = this.ch;
int offset = this.offset, end = this.end;
comma = false;
private static int skipName(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
int quote = jsonReader.ch;
if (jsonReader.checkNameBegin(quote)) {
return jsonReader.offset;
}
switch_:
switch (ch) {
case '-':
case '+':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
boolean sign = ch == '-' || ch == '+';
if (sign) {
int index = IOUtils.indexOfQuote(bytes, quote, offset, end);
if (index == -1) {
throw jsonReader.error("invalid escape character EOI");
}
int ch;
int slashIndex = indexOfSlash(jsonReader, bytes, offset, end);
if (slashIndex == -1 || slashIndex > index) {
offset = index + 1;
ch = offset == end ? EOI : bytes[offset++];
} else {
offset = skipStringEscaped(jsonReader, bytes, slashIndex, quote);
ch = offset == end ? EOI : bytes[offset++];
}
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if (ch != ':') {
throw syntaxError(ch);
}
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
jsonReader.ch = (char) ch;
return offset;
}
private static int skipNumber(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
int ch = jsonReader.ch;
if (ch == '-' || ch == '+') {
if (offset < end) {
ch = bytes[offset++];
} else {
throw jsonReader.error();
}
}
boolean dot = ch == '.';
boolean num = false;
if (!dot && (ch >= '0' && ch <= '9')) {
num = true;
do {
ch = offset == end ? EOI : bytes[offset++];
} while (ch >= '0' && ch <= '9');
}
if (num && (ch == 'L' | ch == 'F' | ch == 'D' | ch == 'B' | ch == 'S')) {
ch = bytes[offset++];
} else {
boolean small = false;
if (ch == '.') {
small = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch >= '0' && ch <= '9') {
ch = offset == end ? EOI : bytes[offset++];
}
}
if (!num && !small) {
throw numberError(offset, ch);
}
if (ch == 'e' || ch == 'E') {
ch = bytes[offset++];
boolean eSign = false;
if (ch == '+' || ch == '-') {
eSign = true;
if (offset < end) {
ch = bytes[offset++];
} else {
throw numberError(offset, ch);
}
}
boolean dot = ch == '.';
boolean num = false;
if (!dot && (ch >= '0' && ch <= '9')) {
num = true;
if (ch >= '0' && ch <= '9') {
do {
ch = offset == end ? EOI : bytes[offset++];
} while (ch >= '0' && ch <= '9');
}
if (num && (ch == 'L' || ch == 'F' || ch == 'D' || ch == 'B' || ch == 'S')) {
ch = bytes[offset++];
}
boolean small = false;
if (ch == '.') {
small = true;
ch = offset == end ? EOI : bytes[offset++];
if (ch >= '0' && ch <= '9') {
do {
ch = offset == end ? EOI : bytes[offset++];
} while (ch >= '0' && ch <= '9');
}
}
if (!num && !small) {
} else if (eSign) {
throw numberError(offset, ch);
}
if (ch == 'e' || ch == 'E') {
ch = bytes[offset++];
boolean eSign = false;
if (ch == '+' || ch == '-') {
eSign = true;
if (offset < end) {
ch = bytes[offset++];
} else {
throw numberError(offset, ch);
}
}
if (ch >= '0' && ch <= '9') {
do {
ch = offset == end ? EOI : bytes[offset++];
} while (ch >= '0' && ch <= '9');
} else if (eSign) {
throw numberError(offset, ch);
}
}
if (ch == 'L' || ch == 'F' || ch == 'D' || ch == 'B' || ch == 'S') {
ch = offset == end ? EOI : bytes[offset++];
}
break;
case 't':
if (offset + 3 > end) {
throw error(offset, ch);
}
if (bytes[offset] != 'r' || bytes[offset + 1] != 'u' || bytes[offset + 2] != 'e') {
throw error(offset, ch);
}
offset += 3;
ch = offset == end ? EOI : bytes[offset++];
break;
case 'f':
if (offset + 4 > end) {
throw error(offset, ch);
}
if (bytes[offset] != 'a' || bytes[offset + 1] != 'l' || bytes[offset + 2] != 's' || bytes[offset + 3] != 'e') {
throw error(offset, ch);
}
offset += 4;
ch = offset == end ? EOI : bytes[offset++];
break;
case 'n':
if (offset + 3 > end) {
throw error(offset, ch);
}
if (bytes[offset] != 'u' || bytes[offset + 1] != 'l' || bytes[offset + 2] != 'l') {
throw error(offset, ch);
}
offset += 3;
ch = offset == end ? EOI : bytes[offset++];
break;
case '"':
case '\'': {
int quote = ch;
ch = bytes[offset++];
for (; ; ) {
if (ch == '\\') {
ch = bytes[offset++];
if (ch == 'u') {
offset += 4;
} else if (ch == 'x') {
offset += 2;
} else if (ch != '\\' && ch != '"') {
char1(ch);
}
ch = bytes[offset++];
continue;
}
if (ch == quote) {
ch = offset == end ? EOI : bytes[offset++];
break;
}
ch = bytes[offset++];
}
break;
}
default:
if (ch == '[') {
next();
for (int i = 0; ; ++i) {
if (this.ch == ']') {
comma = false;
offset = this.offset;
ch = offset == end ? EOI : bytes[offset++];
break switch_;
}
if (i != 0 && !comma) {
throw valueError();
}
comma = false;
skipValue();
}
} else if (ch == '{') {
next();
for (; ; ) {
if (this.ch == '}') {
comma = false;
offset = this.offset;
ch = offset == end ? EOI : bytes[offset++];
break switch_;
}
skipName();
skipValue();
}
} else if (ch == 'S' && nextIfSet()) {
skipValue();
} else {
throw error(offset, ch);
}
ch = this.ch;
offset = this.offset;
break;
if (ch == 'F' || ch == 'D') {
ch = offset == end ? EOI : bytes[offset++];
}
}
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipString(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
int ch = jsonReader.ch;
int quote = ch;
int index = IOUtils.indexOfQuote(bytes, quote, offset, end);
if (index == -1) {
throw jsonReader.error("invalid escape character EOI");
}
int slashIndex = indexOfSlash(jsonReader, bytes, offset, end);
if (slashIndex == -1 || slashIndex > index) {
offset = index + 1;
ch = offset == end ? EOI : bytes[offset++];
} else {
offset = skipStringEscaped(jsonReader, bytes, slashIndex, quote);
ch = offset == end ? EOI : bytes[offset++];
}
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipStringEscaped(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int quote) {
int ch = bytes[offset++];
for (; ; ) {
if (ch == '\\') {
ch = bytes[offset++];
if (ch == 'u') {
offset += 4;
} else if (ch == 'x') {
offset += 2;
} else if (ch != '\\' && ch != '"') {
jsonReader.char1(ch);
}
ch = bytes[offset++];
continue;
}
if (ch == quote) {
return offset;
}
ch = bytes[offset++];
}
}
private static int skipObject(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
offset = next(jsonReader, bytes, offset, end);
for (int i = 0; ; ++i) {
if (jsonReader.ch == '}') {
break;
}
if (i != 0 && !jsonReader.comma) {
throw jsonReader.valueError();
}
offset = skipName(jsonReader, bytes, offset, end);
offset = skipValue(jsonReader, bytes, offset, end);
}
int ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
public static int next(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
int ch = offset >= end ? EOI : bytes[offset++];
while (ch == '\0' || (ch <= ' ' && ((1L << ch) & SPACE) != 0)) {
ch = offset == end ? EOI : bytes[offset++];
}
if (ch < 0) {
jsonReader.char_utf8(ch, offset);
return jsonReader.offset;
}
jsonReader.ch = (char) ch;
if (ch == '/') {
jsonReader.offset = offset;
jsonReader.skipComment();
return jsonReader.offset;
} else {
return offset;
}
}
private static int skipArray(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
offset = next(jsonReader, bytes, offset, end);
for (int i = 0; ; ++i) {
if (jsonReader.ch == ']') {
break;
}
if (i != 0 && !jsonReader.comma) {
throw jsonReader.valueError();
}
offset = skipValue(jsonReader, bytes, offset, end);
}
int ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
@ -4485,15 +4535,158 @@ class JSONReaderUTF8
}
if (!comma && ch != '}' && ch != ']' && ch != EOI) {
throw error(offset, ch);
throw jsonReader.error(offset, ch);
}
if (comma && (ch == '}' || ch == ']' || ch == EOI)) {
throw error(offset, ch);
throw jsonReader.error(offset, ch);
}
this.ch = (char) ch;
this.offset = offset;
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipFalse(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
if (offset + 4 > end || IOUtils.notALSE(bytes, offset)) {
throw jsonReader.error();
}
offset += 4;
int ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipTrue(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
if (offset + 3 > end || IOUtils.notTRUE(bytes, offset - 1)) {
throw jsonReader.error();
}
offset += 3;
int ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipNull(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
if (offset + 3 > end || IOUtils.notNULL(bytes, offset - 1)) {
throw jsonReader.error();
}
offset += 3;
int ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
boolean comma = false;
if (ch == ',') {
comma = true;
ch = offset == end ? EOI : bytes[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : bytes[offset++];
}
if ((ch == '}' || ch == ']' || ch == EOI)) {
throw jsonReader.error(offset, ch);
}
} else if (ch != '}' && ch != ']' && ch != EOI) {
throw jsonReader.error(offset, ch);
}
jsonReader.comma = comma;
jsonReader.ch = (char) ch;
return offset;
}
private static int skipSet(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
if (nextIfSet(jsonReader, bytes, offset, end)) {
return skipArray(jsonReader, bytes, jsonReader.offset, end);
} else {
throw jsonReader.error();
}
}
private static boolean nextIfSet(JSONReaderUTF8 jsonReader, byte[] chars, int offset, int end) {
if (offset + 1 < end && chars[offset] == 'e' && chars[offset + 1] == 't') {
offset += 2;
int ch = offset == end ? EOI : chars[offset++];
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
ch = offset == end ? EOI : chars[offset++];
}
jsonReader.offset = offset;
jsonReader.ch = (char) ch;
return true;
}
return false;
}
private static int skipValue(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
switch (jsonReader.ch) {
case 't':
return skipTrue(jsonReader, bytes, offset, end);
case 'f':
return skipFalse(jsonReader, bytes, offset, end);
case 'n':
return skipNull(jsonReader, bytes, offset, end);
case '"':
case '\'':
return skipString(jsonReader, bytes, offset, end);
case '{':
return skipObject(jsonReader, bytes, offset, end);
case '[':
return skipArray(jsonReader, bytes, offset, end);
case 'S':
return skipSet(jsonReader, bytes, offset, end);
default:
return skipNumber(jsonReader, bytes, offset, end);
}
}
@Override
public final void skipValue() {
this.offset = skipValue(this, bytes, offset, end);
}
@Override
@ -5740,9 +5933,10 @@ class JSONReaderUTF8
}
if (ch < 0) {
char_utf8(ch, offset);
} else {
this.offset = offset;
this.ch = (char) ch;
}
this.offset = offset;
this.ch = (char) ch;
return OffsetDateTime.of(
yy + ((int) ymd & 0xFF),
(int) (ymd >> 24) & 0xFF,

View File

@ -58,6 +58,7 @@ public class ASMUtils {
public static final String TYPE_OBJECT_READER_10 = ObjectReader10.class.getName().replace('.', '/');
public static final String TYPE_OBJECT_READER_11 = ObjectReader11.class.getName().replace('.', '/');
public static final String TYPE_OBJECT_READER_12 = ObjectReader12.class.getName().replace('.', '/');
public static final String TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR = ObjectReaderNoneDefaultConstructor.class.getName().replace('.', '/');
public static final String TYPE_BYTE_ARRAY_VALUE_CONSUMER = ByteArrayValueConsumer.class.getName().replace('.', '/');
public static final String TYPE_CHAR_ARRAY_VALUE_CONSUMER = CharArrayValueConsumer.class.getName().replace('.', '/');
public static final String TYPE_TYPE_UTILS = TypeUtils.class.getName().replace('.', '/');

View File

@ -77,6 +77,8 @@ class Frame {
// Constants to manipulate the DIM field of an abstract type.
private static final int ARRAY_OF = +1 << DIM_SHIFT;
/**
* The constant to be added to an abstract type to get one with one less array dimension.
*/
@ -877,6 +879,7 @@ class Frame {
case Opcodes.IASTORE:
case Opcodes.BASTORE:
case Opcodes.CASTORE:
case Opcodes.AASTORE:
pop(3);
break;
case Opcodes.POP:
@ -980,6 +983,15 @@ class Frame {
push(LONG);
push(TOP);
break;
case Opcodes.I2F:
pop(1);
push(FLOAT);
break;
case Opcodes.I2D:
pop(1);
push(DOUBLE);
push(TOP);
break;
case Opcodes.F2I:
case Opcodes.ARRAYLENGTH:
case Opcodes.INSTANCEOF:
@ -1022,6 +1034,15 @@ class Frame {
case Opcodes.NEW:
push(UNINITIALIZED_KIND | symbolTable.addUninitializedType(argSymbol.value, arg));
break;
case Opcodes.ANEWARRAY:
String arrayElementType = argSymbol.value;
pop();
if (arrayElementType.charAt(0) == '[') {
push(symbolTable, '[' + arrayElementType);
} else {
push(ARRAY_OF | REFERENCE_KIND | symbolTable.addType(arrayElementType));
}
break;
case Opcodes.CHECKCAST:
String castType = argSymbol.value;
pop();

View File

@ -293,6 +293,14 @@ public final class MethodWriter {
visitInsn(Opcodes.I2L);
}
public void i2f() {
visitInsn(Opcodes.I2F);
}
public void i2d() {
visitInsn(Opcodes.I2D);
}
public void lxor() {
visitInsn(Opcodes.LXOR);
}
@ -305,6 +313,10 @@ public final class MethodWriter {
visitInsn(Opcodes.AALOAD);
}
public void aastore() {
visitInsn(Opcodes.AASTORE);
}
private void visitInsn(final int opcode) {
lastBytecodeOffset = code.length;
// Add the instruction to the bytecode of the method.
@ -455,6 +467,10 @@ public final class MethodWriter {
visitTypeInsn(Opcodes.NEW, type);
}
public void anewArray(final String type) {
visitTypeInsn(Opcodes.ANEWARRAY, type);
}
public void instanceOf(final String type) {
visitTypeInsn(Opcodes.INSTANCEOF, type);
}

View File

@ -91,6 +91,7 @@ public interface Opcodes {
int DSTORE = 57; // -
int ASTORE = 58; // -
int IASTORE = 79; // visitInsn
int AASTORE = 83; // -
int BASTORE = 84; // -
int CASTORE = 85; // -
int POP = 87; // visitInsn
@ -126,6 +127,8 @@ public interface Opcodes {
int LXOR = 131; // -
int IINC = 132; // visitIincInsn
int I2L = 133; // visitInsn
int I2F = 134; // -
int I2D = 135; // -
int L2I = 136; //
int F2I = 139; // -
int F2L = 140; // -
@ -173,6 +176,8 @@ public interface Opcodes {
int INVOKESTATIC = 184; // -
int INVOKEINTERFACE = 185; // -
int NEW = 187; // visitTypeInsn
int NEWARRAY = 188; // -
int ANEWARRAY = 189; // -
int ARRAYLENGTH = 190; // visitInsn
int ATHROW = 191; // -
int CHECKCAST = 192; // visitTypeInsn

View File

@ -70,8 +70,10 @@ public abstract class FieldReader<T>
this.fieldName = fieldName;
this.fieldType = fieldType;
this.fieldClass = fieldClass;
this.fieldClassSerializable = fieldClass != null && (Serializable.class.isAssignableFrom(fieldClass)
|| Modifier.isInterface(fieldClass.getModifiers()));
this.fieldClassSerializable = fieldClass != null
&& (Serializable.class.isAssignableFrom(fieldClass)
|| Modifier.isInterface(fieldClass.getModifiers())
|| BeanUtils.isRecord(fieldClass));
this.features = features;
this.fieldNameHash = Fnv.hashCode64(fieldName);
this.fieldNameHashLCase = Fnv.hashCode64LCase(fieldName);

View File

@ -0,0 +1,37 @@
package com.alibaba.fastjson2.reader;
import com.alibaba.fastjson2.schema.JSONSchema;
import com.alibaba.fastjson2.util.Fnv;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.Locale;
public class FieldReaderListParam
extends FieldReaderList {
final Parameter parameter;
final String paramName;
final long paramNameHash;
public FieldReaderListParam(
String fieldName,
Type fieldType,
String paramName,
Parameter parameter,
Class fieldClass,
Type itemType,
Class itemClass,
int ordinal,
long features,
String format,
Locale locale,
Object defaultValue,
JSONSchema schema
) {
super(fieldName, fieldType, fieldClass, itemType, itemClass, ordinal, features, format, locale, defaultValue, schema, null, null, null);
this.paramName = paramName;
this.paramNameHash = Fnv.hashCode64(paramName);
this.parameter = parameter;
}
}

View File

@ -445,6 +445,21 @@ public class ObjectReaderAdapter<T>
return fieldReader;
}
protected final void readFieldValue(long hashCode, JSONReader jsonReader, long features, Map<Long, Object> map) {
FieldReader fieldReader = getFieldReader(hashCode);
if (fieldReader == null
&& jsonReader.isSupportSmartMatch(this.features | features)) {
long hashCodeL = jsonReader.getNameHashCodeLCase();
fieldReader = getFieldReaderLCase(hashCodeL);
}
if (fieldReader != null) {
map.put(hashCode, fieldReader.readFieldValue(jsonReader));
} else {
jsonReader.skipValue();
}
}
protected final void readFieldValue(long hashCode, JSONReader jsonReader, long features, Object object) {
FieldReader fieldReader = getFieldReader(hashCode);
if (fieldReader == null

View File

@ -10,6 +10,7 @@ import com.alibaba.fastjson2.util.TypeUtils;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
@ -96,6 +97,26 @@ public abstract class ObjectReaderBean<T>
processExtra(jsonReader, object, 0);
}
protected void processExtra(JSONReader jsonReader, Map<Long, Object> map, long features) {
if ((jsonReader.features(this.features | features) & JSONReader.Feature.SupportSmartMatch.mask) != 0) {
String fieldName = jsonReader.getFieldName();
if (fieldName.startsWith("is")) {
String fieldName1 = fieldName.substring(2);
long hashCode64LCase = Fnv.hashCode64LCase(fieldName1);
FieldReader fieldReader = getFieldReaderLCase(hashCode64LCase);
if (fieldReader != null) {
Class fieldClass = fieldReader.fieldClass;
if (fieldClass == Boolean.class || fieldClass == boolean.class) {
map.put(fieldReader.fieldNameHash, fieldReader.readFieldValue(jsonReader));
return;
}
}
}
}
jsonReader.skipValue();
}
protected void processExtra(JSONReader jsonReader, Object object, long features) {
if ((jsonReader.features(this.features | features) & JSONReader.Feature.SupportSmartMatch.mask) != 0) {
String fieldName = jsonReader.getFieldName();

View File

@ -1273,18 +1273,14 @@ public class ObjectReaderCreator {
creatorConstructor.getParameters(),
parameterNames
);
return new ObjectReaderNoneDefaultConstructor(
return createNoneDefaultConstructorObjectReader(
objectClass,
beanInfo.typeKey,
beanInfo.typeName,
beanInfo.readerFeatures,
beanInfo,
constructorFunction,
alternateConstructors,
parameterNames,
paramFieldReaders,
fieldReaderArray,
null,
null
fieldReaderArray
);
}
}
@ -1344,6 +1340,30 @@ public class ObjectReaderCreator {
return objectReader;
}
protected <T> ObjectReaderNoneDefaultConstructor createNoneDefaultConstructorObjectReader(
Class<T> objectClass,
BeanInfo beanInfo,
Function<Map<Long, Object>, T> constructorFunction,
List<Constructor> alternateConstructors,
String[] parameterNames,
FieldReader[] paramFieldReaders,
FieldReader[] fieldReaderArray
) {
return new ObjectReaderNoneDefaultConstructor(
objectClass,
beanInfo.typeKey,
beanInfo.typeName,
beanInfo.readerFeatures,
constructorFunction,
alternateConstructors,
parameterNames,
paramFieldReaders,
fieldReaderArray,
null,
null
);
}
public <T> FieldReader[] createFieldReaders(Class<T> objectClass) {
return createFieldReaders(
objectClass,
@ -2199,6 +2219,33 @@ public class ObjectReaderCreator {
fieldClassResolved = fieldClass;
}
Type itemType = null;
Class itemClass = null;
if (fieldTypeResolved instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) fieldTypeResolved;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
if (actualTypeArguments.length == 1) {
itemType = actualTypeArguments[0];
itemClass = TypeUtils.getClass(itemClass);
}
}
if (fieldClassResolved != null && Collection.class.isAssignableFrom(fieldClassResolved) && itemType != null) {
return new FieldReaderListParam(
fieldName,
fieldTypeResolved,
paramName,
parameter,
fieldClassResolved,
itemType,
itemClass,
ordinal,
features,
format,
locale,
defaultValue,
schema);
}
return new FieldReaderObjectParam(
fieldName,
fieldTypeResolved,

View File

@ -23,7 +23,6 @@ import java.util.function.*;
import static com.alibaba.fastjson2.internal.CodeGenUtils.fieldReader;
import static com.alibaba.fastjson2.internal.asm.ASMUtils.*;
import static com.alibaba.fastjson2.internal.asm.Opcodes.*;
import static com.alibaba.fastjson2.reader.ObjectReader.HASH_TYPE;
import static com.alibaba.fastjson2.util.JDKUtils.*;
@ -46,10 +45,12 @@ public class ObjectReaderCreatorASM
static final String METHOD_DESC_READ_FIELD_VALUE = "(" + DESC_JSON_READER + "Ljava/lang/Object;)V";
static final String GET_FIELD_READER_UL = "(J" + DESC_JSON_READER + "J)" + DESC_FIELD_READER;
static final String READ_FIELD_READER_UL = "(J" + DESC_JSON_READER + "JLjava/lang/Object;)V";
static final String READ_FIELD_READER_MAP = "(J" + DESC_JSON_READER + "JLjava/util/Map;)V";
static final String METHOD_DESC_ADD_RESOLVE_TASK = "(" + DESC_JSON_READER + "Ljava/lang/Object;Ljava/lang/String;)V";
static final String METHOD_DESC_ADD_RESOLVE_TASK_2 = "(" + DESC_JSON_READER + "Ljava/util/List;ILjava/lang/String;)V";
static final String METHOD_DESC_CHECK_ARRAY_AUTO_TYPE = "(" + DESC_JSON_READER + ")" + DESC_OBJECT_READER;
static final String METHOD_DESC_PROCESS_EXTRA = "(" + DESC_JSON_READER + "Ljava/lang/Object;J)V";
static final String METHOD_DESC_PROCESS_EXTRA_2 = "(" + DESC_JSON_READER + "Ljava/util/Map;J)V";
static final String METHOD_DESC_JSON_READER_CHECK_ARRAY_AUTO_TYPE = "(" + DESC_JSON_READER + "J)" + DESC_OBJECT_READER;
static final String METHOD_DESC_READ_ARRAY_MAPPING_JSONB_OBJECT0 = "(" + DESC_JSON_READER + "Ljava/lang/Object;I)V";
@ -337,6 +338,150 @@ public class ObjectReaderCreatorASM
);
}
@Override
protected <T> ObjectReaderNoneDefaultConstructor createNoneDefaultConstructorObjectReader(
Class<T> objectClass,
BeanInfo beanInfo,
Function<Map<Long, Object>, T> constructorFunction,
List<Constructor> alternateConstructors,
String[] parameterNames,
FieldReader[] paramFieldReaders,
FieldReader[] fieldReaderArray
) {
ObjectReaderNoneDefaultConstructor objectReaderAdapter = new ObjectReaderNoneDefaultConstructor(
objectClass,
beanInfo.typeKey,
beanInfo.typeName,
beanInfo.readerFeatures,
constructorFunction,
alternateConstructors,
parameterNames,
paramFieldReaders,
fieldReaderArray,
null,
null
);
boolean match = true;
if (beanInfo.autoTypeBeforeHandler != null
|| fieldReaderArray.length != 0
|| !(constructorFunction instanceof ConstructorFunction)
|| !alternateConstructors.isEmpty()
|| classLoader.isExternalClass(objectClass)
|| (beanInfo.readerFeatures & JSONReader.Feature.SupportAutoType.mask) != 0
) {
match = false;
}
if (match) {
for (FieldReader fieldReader : paramFieldReaders) {
if (fieldReader.getInitReader() != null) {
match = false;
break;
}
if (fieldReader.defaultValue != null || fieldReader.schema != null) {
match = false;
break;
}
Class fieldClass = fieldReader.fieldClass;
if (fieldClass != null && (!Modifier.isPublic(fieldClass.getModifiers()) || classLoader.isExternalClass(fieldClass))) {
match = false;
break;
}
if (fieldReader instanceof FieldReaderMapField
&& ((FieldReaderMapField<?>) fieldReader).arrayToMapKey != null) {
match = false;
break;
}
if (fieldReader instanceof FieldReaderMapMethod
&& ((FieldReaderMapMethod<?>) fieldReader).arrayToMapKey != null) {
match = false;
break;
}
}
}
if (!match) {
return objectReaderAdapter;
}
boolean externalClass = objectClass != null && classLoader.isExternalClass(objectClass);
ClassWriter cw = new ClassWriter(
(e) -> objectClass.getName().equals(e) ? objectClass : null
);
beanInfo.readerFeatures |= FieldInfo.DISABLE_REFERENCE_DETECT;
ObjectWriteContext context = new ObjectWriteContext(beanInfo, objectClass, cw, externalClass, paramFieldReaders, null);
context.objectReaderAdapter = objectReaderAdapter;
genFields(paramFieldReaders, cw, TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR);
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, context.classNameType, TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR, new String[]{});
{
String MD_INIT = "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;JLjava/util/function/Function;Ljava/util/List;[Ljava/lang/String;[Lcom/alibaba/fastjson2/reader/FieldReader;[Lcom/alibaba/fastjson2/reader/FieldReader;[Ljava/lang/Class;[Ljava/lang/String;)V";
MethodWriter mw = cw.visitMethod(
Opcodes.ACC_PUBLIC,
"<init>",
MD_INIT,
fieldReaderArray.length <= 12 ? 32 : 128);
mw.aload(THIS);
mw.aload(1); // CLASS
mw.aload(2); // TYPE_KEY
mw.aload(3); // TYPE_NAME
mw.lload(4); // FEATURES
mw.aload(6); // CREATOR
mw.aload(7); // alternateConstructors
mw.aload(8); // paramNames
mw.aload(9); // paramFieldReaders
mw.aload(10); // setterFieldReaders
mw.aload(11); // seeAlso
mw.aload(12); // seeAlsoNames
mw.invokespecial(TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR, "<init>", MD_INIT);
int FIELD_READER_ARRAY = 9;
genInitFields(paramFieldReaders, context.classNameType, true, THIS, FIELD_READER_ARRAY, mw, TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR);
mw.return_();
mw.visitMaxs(3, 3);
}
String TYPE_OBJECT = objectClass == null ? ASMUtils.TYPE_OBJECT : ASMUtils.type(objectClass);
genMethodReadObject(context, beanInfo.readerFeatures, TYPE_OBJECT, paramFieldReaders, cw, context.classNameType);
byte[] code = cw.toByteArray();
try {
Class<?> readerClass = classLoader.defineClassPublic(context.classNameFull, code, 0, code.length);
Constructor<?> constructor = readerClass.getConstructors()[0];
return (ObjectReaderNoneDefaultConstructor) constructor
.newInstance(
objectClass,
beanInfo.typeKey,
beanInfo.typeName,
beanInfo.readerFeatures,
constructorFunction,
alternateConstructors,
parameterNames,
paramFieldReaders,
fieldReaderArray,
null,
null
);
} catch (Throwable e) {
throw new JSONException(
"create objectReader error"
+ (objectClass == null ? "" : ", objectType " + objectClass.getTypeName()), e);
}
}
@Override
public <T> ObjectReader<T> createObjectReader(
Class<T> objectClass,
@ -401,34 +546,7 @@ public class ObjectReaderCreatorASM
(e) -> objectClass.getName().equals(e) ? objectClass : null
);
ObjectWriteContext context = new ObjectWriteContext(beanInfo, objectClass, cw, externalClass, fieldReaderArray);
String className = "ORG_" + seed.incrementAndGet() + "_" + fieldReaderArray.length + (objectClass == null ? "" : "_" + objectClass.getSimpleName());
String classNameType;
String classNameFull;
Package pkg = ObjectReaderCreatorASM.class.getPackage();
if (pkg != null) {
String packageName = pkg.getName();
int packageNameLength = packageName.length();
int charsLength = packageNameLength + 1 + className.length();
char[] chars = new char[charsLength];
packageName.getChars(0, packageName.length(), chars, 0);
chars[packageNameLength] = '.';
className.getChars(0, className.length(), chars, packageNameLength + 1);
classNameFull = new String(chars);
chars[packageNameLength] = '/';
for (int i = 0; i < packageNameLength; ++i) {
if (chars[i] == '.') {
chars[i] = '/';
}
}
classNameType = new String(chars);
} else {
classNameType = className;
classNameFull = className;
}
ObjectWriteContext context = new ObjectWriteContext(beanInfo, objectClass, cw, externalClass, fieldReaderArray, defaultConstructor);
final boolean generatedFields = fieldReaderArray.length < 128;
@ -479,7 +597,7 @@ public class ObjectReaderCreatorASM
genFields(fieldReaderArray, cw, objectReaderSuper);
}
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, classNameType, objectReaderSuper, new String[]{});
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, context.classNameType, objectReaderSuper, new String[]{});
{
final int CLASS = 1, SUPPLIER = 2, FIELD_READER_ARRAY = 3;
@ -504,7 +622,7 @@ public class ObjectReaderCreatorASM
mw.aload(FIELD_READER_ARRAY);
mw.invokespecial(objectReaderSuper, "<init>", METHOD_DESC_ADAPTER_INIT);
genInitFields(fieldReaderArray, classNameType, generatedFields, THIS, FIELD_READER_ARRAY, mw, objectReaderSuper);
genInitFields(fieldReaderArray, context.classNameType, generatedFields, THIS, FIELD_READER_ARRAY, mw, objectReaderSuper);
mw.return_();
mw.visitMaxs(3, 3);
@ -559,16 +677,16 @@ public class ObjectReaderCreatorASM
boolean disableArrayMapping = context.disableSupportArrayMapping();
boolean disableJSONB = context.disableJSONB();
ObjectReaderAdapter objectReaderAdapter = new ObjectReaderAdapter(objectClass, beanInfo.typeKey, beanInfo.typeName, readerFeatures, null, supplier, null, fieldReaderArray);
context.objectReaderAdapter = new ObjectReaderAdapter(objectClass, beanInfo.typeKey, beanInfo.typeName, readerFeatures, null, supplier, null, fieldReaderArray);
if (!disableJSONB) {
genMethodReadJSONBObject(context, defaultConstructor, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, classNameType, objectReaderAdapter);
genMethodReadJSONBObject(context, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, context.classNameType);
if (!disableArrayMapping) {
genMethodReadJSONBObjectArrayMapping(context, defaultConstructor, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, classNameType, objectReaderAdapter);
genMethodReadJSONBObjectArrayMapping(context, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, context.classNameType);
}
}
genMethodReadObject(context, defaultConstructor, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, classNameType, objectReaderAdapter);
genMethodReadObject(context, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, context.classNameType);
if (objectReaderSuper == TYPE_OBJECT_READER_ADAPTER
|| objectReaderSuper == TYPE_OBJECT_READER_1
@ -584,14 +702,14 @@ public class ObjectReaderCreatorASM
|| objectReaderSuper == TYPE_OBJECT_READER_11
|| objectReaderSuper == TYPE_OBJECT_READER_12
) {
genMethodGetFieldReader(fieldReaderArray, cw, classNameType, objectReaderAdapter);
genMethodGetFieldReaderLCase(fieldReaderArray, cw, classNameType, objectReaderAdapter);
genMethodGetFieldReader(fieldReaderArray, cw, context.classNameType, context.objectReaderAdapter);
genMethodGetFieldReaderLCase(fieldReaderArray, cw, context.classNameType, context.objectReaderAdapter);
}
}
byte[] code = cw.toByteArray();
try {
Class<?> readerClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length);
Class<?> readerClass = classLoader.defineClassPublic(context.classNameFull, code, 0, code.length);
Constructor<?> constructor = readerClass.getConstructors()[0];
return (ObjectReaderBean) constructor
@ -839,7 +957,7 @@ public class ObjectReaderCreatorASM
MethodWriter mw,
String objectReaderSuper
) {
if (objectReaderSuper != TYPE_OBJECT_READER_ADAPTER || !generatedFields) {
if ((objectReaderSuper != TYPE_OBJECT_READER_ADAPTER && objectReaderSuper != TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR) || !generatedFields) {
return;
}
@ -853,7 +971,7 @@ public class ObjectReaderCreatorASM
}
private void genFields(FieldReader[] fieldReaderArray, ClassWriter cw, String objectReaderSuper) {
if (objectReaderSuper == TYPE_OBJECT_READER_ADAPTER) {
if (objectReaderSuper == TYPE_OBJECT_READER_ADAPTER || objectReaderSuper == TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR) {
for (int i = 0; i < fieldReaderArray.length; i++) {
FieldWriter fv = cw.visitField(Opcodes.ACC_PUBLIC, fieldReader(i), DESC_FIELD_READER);
}
@ -873,16 +991,16 @@ public class ObjectReaderCreatorASM
private <T> void genMethodReadJSONBObject(
ObjectWriteContext context,
Constructor defaultConstructor,
long readerFeatures,
String TYPE_OBJECT,
FieldReader[] fieldReaderArray,
ClassWriter cw,
String classNameType,
ObjectReaderAdapter objectReaderAdapter
String classNameType
) {
Class objectClass = context.objectClass;
boolean fieldBased = (readerFeatures & JSONReader.Feature.FieldBased.mask) != 0;
Constructor defaultConstructor = context.defaultConstructor;
ObjectReaderAdapter objectReaderAdapter = context.objectReaderAdapter;
MethodWriter mw = cw.visitMethod(Opcodes.ACC_PUBLIC,
"readJSONBObject",
@ -961,16 +1079,13 @@ public class ObjectReaderCreatorASM
mw.visitLabel(object_);
}
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased, defaultConstructor, objectReaderAdapter.creator);
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased);
mw.astore(OBJECT);
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "nextIfObjectStart", "()Z");
mw.pop();
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased, defaultConstructor, objectReaderAdapter.creator);
mw.astore(OBJECT);
// for (int i = 0; i < entry_cnt; ++i) {
Label for_start_i_ = new Label(), for_end_i_ = new Label(), for_inc_i_ = new Label();
if (!disableAutoType) {
@ -1259,14 +1374,13 @@ public class ObjectReaderCreatorASM
private <T> void genMethodReadJSONBObjectArrayMapping(
ObjectWriteContext context,
Constructor defaultConstructor,
long readerFeatures,
String TYPE_OBJECT,
FieldReader[] fieldReaderArray,
ClassWriter cw,
String classNameType,
ObjectReaderAdapter objectReaderAdapter
String classNameType
) {
Constructor defaultConstructor = context.defaultConstructor;
boolean fieldBased = (readerFeatures & JSONReader.Feature.FieldBased.mask) != 0;
MethodWriter mw = cw.visitMethod(Opcodes.ACC_PUBLIC,
@ -1305,7 +1419,7 @@ public class ObjectReaderCreatorASM
mw.visitLabel(notNull_);
}
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased, defaultConstructor, objectReaderAdapter.creator);
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased);
mw.astore(OBJECT);
Label fieldEnd_ = new Label(), entryCountMatch_ = new Label();
@ -1386,13 +1500,11 @@ public class ObjectReaderCreatorASM
private <T> void genMethodReadObject(
ObjectWriteContext context,
Constructor defaultConstructor,
long readerFeatures,
String TYPE_OBJECT,
FieldReader[] fieldReaderArray,
ClassWriter cw,
String classNameType,
ObjectReaderAdapter objectReaderAdapter
String classNameType
) {
boolean fieldBased = (readerFeatures & JSONReader.Feature.FieldBased.mask) != 0;
@ -1493,13 +1605,62 @@ public class ObjectReaderCreatorASM
mw.ifeq(notNull_);
mw.aconst_null();
mw.astore(OBJECT);
mw.goto_(end_);
mw.areturn();
mw.visitLabel(notNull_);
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased, defaultConstructor, objectReaderAdapter.creator);
mw.astore(OBJECT);
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
Label L0 = new Label(), L1 = new Label();
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "hasAutoTypeBeforeHandler", "()Z");
mw.ifne(L0);
mw.lload(FEATURES);
mw.visitLdcInsn(JSONReader.Feature.SupportSmartMatch.mask | JSONReader.Feature.SupportAutoType.mask);
mw.land();
mw.lconst_0();
mw.lcmp();
mw.ifeq(L1);
mw.visitLabel(L0);
mw.aload(THIS);
mw.aload(JSON_READER);
mw.aload(FIELD_TYPE);
mw.aload(FIELD_NAME);
mw.lload(FEATURES);
mw.invokespecial(TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR, "readObject", METHOD_DESC_READ_OBJECT);
mw.areturn();
mw.visitLabel(L1);
for (FieldReader fieldReader : fieldReaderArray) {
Class fieldClass = fieldReader.fieldClass;
int var = mwc.var(fieldReader);
if (fieldClass == byte.class || fieldClass == short.class || fieldClass == int.class || fieldClass == boolean.class || fieldClass == char.class) {
mw.iconst_0();
mw.istore(var);
} else if (fieldClass == long.class) {
mw.lconst_0();
mw.lstore(var);
} else if (fieldClass == float.class) {
mw.iconst_0();
mw.i2f();
mw.fstore(var);
} else if (fieldClass == double.class) {
mw.iconst_0();
mw.i2d();
mw.dstore(var);
} else {
mw.aconst_null();
mw.astore(var);
}
}
mw.aconst_null();
mw.astore(mwc.var("map"));
} else {
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased);
mw.astore(OBJECT);
}
// for (int i = 0; i < entry_cnt; ++i) {
Label for_start_i_ = new Label(), for_end_i_ = new Label(), for_inc_i_ = new Label();
@ -1556,17 +1717,24 @@ public class ObjectReaderCreatorASM
mw.visitLabel(hashCode64Start);
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "readFieldNameHashCode", "()J");
mw.dup2();
mw.lstore(HASH_CODE64);
mw.visitLdcInsn(-1L);
mw.lcmp();
mw.ifeq(for_end_i_);
if (switchGen && context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "skipName", "()Z");
mw.pop();
} else {
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "readFieldNameHashCode", "()J");
mw.dup2();
mw.lstore(HASH_CODE64);
mw.visitLdcInsn(-1L);
mw.lcmp();
mw.ifeq(for_end_i_);
}
mw.visitLabel(hashCode64End);
if (!disableAutoType) {
if (!disableAutoType && !(context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor)) {
Label noneAutoType_ = new Label();
// if (i != 0 && hash == HASH_TYPE && jsonReader.isSupportAutoType())
@ -1598,19 +1766,24 @@ public class ObjectReaderCreatorASM
// continue
if (switchGen) {
mw.aload(THIS);
mw.lload(HASH_CODE64);
mw.aload(JSON_READER);
mw.lload(FEATURES);
mw.aload(OBJECT);
mw.invokevirtual(TYPE_OBJECT_READER_ADAPTER, "readFieldValue", READ_FIELD_READER_UL);
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "skipValue", "()V");
} else {
mw.aload(THIS);
mw.lload(HASH_CODE64);
mw.aload(JSON_READER);
mw.lload(FEATURES);
mw.aload(OBJECT);
mw.invokevirtual(TYPE_OBJECT_READER_ADAPTER, "readFieldValue", READ_FIELD_READER_UL);
}
mw.goto_(for_inc_i_); // continue
} else if (fieldReaderArray.length > 6) {
// use switch
Map<Integer, List<Long>> map = new TreeMap();
for (int i = 0; i < objectReaderAdapter.hashCodes.length; i++) {
long hashCode64 = objectReaderAdapter.hashCodes[i];
for (int i = 0; i < context.objectReaderAdapter.hashCodes.length; i++) {
long hashCode64 = context.objectReaderAdapter.hashCodes[i];
int hashCode32 = (int) (hashCode64 ^ (hashCode64 >>> 32));
List<Long> hashCode64List = map.computeIfAbsent(hashCode32, k -> new ArrayList<>());
hashCode64List.add(hashCode64);
@ -1656,8 +1829,8 @@ public class ObjectReaderCreatorASM
mw.lcmp();
mw.ifne(next);
int m = Arrays.binarySearch(objectReaderAdapter.hashCodes, hashCode64);
int index = objectReaderAdapter.mapping[m];
int m = Arrays.binarySearch(context.objectReaderAdapter.hashCodes, hashCode64);
int index = context.objectReaderAdapter.mapping[m];
FieldReader fieldReader = fieldReaderArray[index];
@ -1824,11 +1997,19 @@ public class ObjectReaderCreatorASM
mw.visitLabel(processExtra_);
}
if (!switchGen) {
mw.aload(THIS);
mw.aload(JSON_READER);
mw.aload(OBJECT);
mw.lload(FEATURES);
mw.invokevirtual(TYPE_OBJECT_READER_ADAPTER, "processExtra", METHOD_DESC_PROCESS_EXTRA);
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
mw.aload(THIS);
mw.aload(JSON_READER);
mw.aload(mwc.var("map"));
mw.lload(FEATURES);
mw.invokevirtual(TYPE_OBJECT_READER_ADAPTER, "processExtra", METHOD_DESC_PROCESS_EXTRA_2);
} else {
mw.aload(THIS);
mw.aload(JSON_READER);
mw.aload(OBJECT);
mw.lload(FEATURES);
mw.invokevirtual(TYPE_OBJECT_READER_ADAPTER, "processExtra", METHOD_DESC_PROCESS_EXTRA);
}
mw.goto_(for_inc_i_); // continue
}
@ -1842,7 +2023,60 @@ public class ObjectReaderCreatorASM
mw.visitLabel(end_);
mw.aload(OBJECT);
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
ObjectReaderNoneDefaultConstructor objectReaderNoneDefaultConstructor = (ObjectReaderNoneDefaultConstructor) context.objectReaderAdapter;
boolean constructDirect = true;
if (classLoader.isExternalClass(context.objectClass)
|| (objectReaderNoneDefaultConstructor.constructor != null && !Modifier.isPublic(objectReaderNoneDefaultConstructor.constructor.getModifiers()))
|| (context.objectClass != null && !Modifier.isPublic(context.objectClass.getModifiers()))
) {
constructDirect = false;
}
if (constructDirect) {
mw.new_(TYPE_OBJECT);
mw.dup();
StringBuilder buf = new StringBuilder().append("(");
for (FieldReader fieldReader : fieldReaderArray) {
mw.loadLocal(fieldReader.fieldClass, mwc.var(fieldReader));
buf.append(ASMUtils.desc(fieldReader.fieldClass));
}
buf.append(")V");
mw.invokespecial(TYPE_OBJECT, "<init>", buf.toString());
} else {
mw.aload(THIS);
mw.iconst_n(fieldReaderArray.length);
mw.anewArray("java/lang/Object");
for (int i = 0; i < fieldReaderArray.length; i++) {
FieldReader fieldReader = fieldReaderArray[i];
mw.dup();
mw.iconst_n(i);
mw.loadLocal(fieldReader.fieldClass, mwc.var(fieldReader));
if (fieldReader.fieldClass == int.class) {
mw.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
} else if (fieldReader.fieldClass == long.class) {
mw.invokestatic("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
} else if (fieldReader.fieldClass == float.class) {
mw.invokestatic("java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
} else if (fieldReader.fieldClass == double.class) {
mw.invokestatic("java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
} else if (fieldReader.fieldClass == boolean.class) {
mw.invokestatic("java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
} else if (fieldReader.fieldClass == short.class) {
mw.invokestatic("java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
} else if (fieldReader.fieldClass == byte.class) {
mw.invokestatic("java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
} else if (fieldReader.fieldClass == char.class) {
mw.invokestatic("java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
}
mw.aastore();
}
mw.invokevirtual(TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR, "createInstance", "([Ljava/lang/Object;)Ljava/lang/Object;");
}
mw.areturn();
} else {
mw.aload(OBJECT);
}
mw.areturn();
mw.visitMaxs(5, 10);
@ -2661,10 +2895,10 @@ public class ObjectReaderCreatorASM
String classNameType,
String TYPE_OBJECT,
int FEATURES,
boolean fieldBased,
Constructor defaultConstructor,
Supplier creator
boolean fieldBased
) {
Constructor defaultConstructor = context.defaultConstructor;
Supplier creator = context.objectReaderAdapter.creator;
Class objectClass = context.objectClass;
final int JSON_READER = 1;
@ -2687,7 +2921,7 @@ public class ObjectReaderCreatorASM
mw.checkcast(TYPE_OBJECT);
}
} else {
newObject(mw, TYPE_OBJECT, defaultConstructor);
newObject(mw, TYPE_OBJECT, context.defaultConstructor);
}
if (context.hasStringField) {
@ -2755,7 +2989,9 @@ public class ObjectReaderCreatorASM
String TYPE_FIELD_CLASS = ASMUtils.type(fieldClass);
String DESC_FIELD_CLASS = ASMUtils.desc(fieldClass);
mw.aload(OBJECT);
if (!(context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor)) {
mw.aload(OBJECT);
}
int fieldModifier = 0;
if ((fieldBased || method == null) && field != null) {
fieldModifier = field.getModifiers();
@ -2919,7 +3155,9 @@ public class ObjectReaderCreatorASM
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "skipValue", "()V");
mw.pop();
if (!(context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor)) {
mw.pop();
}
mw.goto_(endSet_);
mw.visitLabel(endIgnoreCheck_);
@ -2962,7 +3200,8 @@ public class ObjectReaderCreatorASM
fieldFeatures,
itemType,
TYPE_FIELD_CLASS,
context
context,
fieldBased
);
} else {
final String FIELD_OBJECT_READER = fieldObjectReader(i);
@ -3131,6 +3370,11 @@ public class ObjectReaderCreatorASM
mw.visitVarInsn(LOAD, FIELD_VALUE);
mw.invokevirtual("sun/misc/Unsafe", methodName, methodDes);
}
} else if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
if (!fieldClass.isPrimitive()) {
mw.checkcast(ASMUtils.type(fieldClass));
}
mw.storeLocal(fieldClass, mwc.var(fieldReader));
} else {
boolean invokeFieldReaderAccept = context.externalClass || method == null || !context.publicClass;
@ -3526,7 +3770,8 @@ public class ObjectReaderCreatorASM
long fieldFeatures,
Type itemType,
String TYPE_FIELD_CLASS,
ObjectWriteContext context
ObjectWriteContext context,
boolean fieldBased
) {
if (itemType == null) {
itemType = Object.class;
@ -3536,7 +3781,12 @@ public class ObjectReaderCreatorASM
String ITEM_OBJECT_READER = fieldItemObjectReader(i);
MethodWriter mw = mwc.mw;
Integer LIST = mwc.var(fieldClass);
int LIST;
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
LIST = mwc.var(fieldReader);
} else {
LIST = mwc.var(fieldClass);
}
Integer AUTO_TYPE_OBJECT_READER = mwc.var(ObjectReader.class);
String LIST_TYPE = fieldClass.isInterface() ? "java/util/ArrayList" : TYPE_FIELD_CLASS;
@ -3833,13 +4083,18 @@ public class ObjectReaderCreatorASM
final boolean hasStringField;
final int fieldNameLengthMin;
final int fieldNameLengthMax;
final String classNameType;
final String classNameFull;
final Constructor defaultConstructor;
ObjectReaderAdapter objectReaderAdapter;
public ObjectWriteContext(
BeanInfo beanInfo,
Class objectClass,
ClassWriter cw,
boolean externalClass,
FieldReader[] fieldReaders
FieldReader[] fieldReaders,
Constructor defaultConstructor
) {
this.beanInfo = beanInfo;
this.objectClass = objectClass;
@ -3847,6 +4102,7 @@ public class ObjectReaderCreatorASM
this.publicClass = objectClass == null || Modifier.isPublic(objectClass.getModifiers());
this.externalClass = externalClass;
this.fieldReaders = fieldReaders;
this.defaultConstructor = defaultConstructor;
int fieldNameLengthMin = 0, fieldNameLengthMax = 0;
boolean hasStringField = false;
@ -3876,6 +4132,31 @@ public class ObjectReaderCreatorASM
this.hasStringField = hasStringField;
this.fieldNameLengthMin = fieldNameLengthMin;
this.fieldNameLengthMax = fieldNameLengthMax;
String className = "ORG_" + seed.incrementAndGet() + "_" + fieldReaders.length + (objectClass == null ? "" : "_" + objectClass.getSimpleName());
Package pkg = ObjectReaderCreatorASM.class.getPackage();
if (pkg != null) {
String packageName = pkg.getName();
int packageNameLength = packageName.length();
int charsLength = packageNameLength + 1 + className.length();
char[] chars = new char[charsLength];
packageName.getChars(0, packageName.length(), chars, 0);
chars[packageNameLength] = '.';
className.getChars(0, className.length(), chars, packageNameLength + 1);
classNameFull = new String(chars);
chars[packageNameLength] = '/';
for (int i = 0; i < packageNameLength; ++i) {
if (chars[i] == '.') {
chars[i] = '/';
}
}
classNameType = new String(chars);
} else {
classNameType = className;
classNameFull = className;
}
}
public boolean disableSupportArrayMapping() {
@ -4296,6 +4577,24 @@ public class ObjectReaderCreatorASM
return var;
}
int var(FieldReader fieldReader) {
return var("_param_" + fieldReader.fieldName, fieldReader.fieldClass);
}
int var(String name, Class type) {
Integer var = variants.get(name);
if (var == null) {
var = maxVariant;
variants.put(name, var);
if (type == long.class || type == double.class) {
maxVariant += 2;
} else {
maxVariant += 1;
}
}
return var;
}
int var2(Object key) {
Integer var = variants.get(key);
if (var == null) {

View File

@ -5,6 +5,7 @@ import com.alibaba.fastjson2.util.Fnv;
import com.alibaba.fastjson2.util.TypeUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Function;
@ -16,8 +17,9 @@ public class ObjectReaderNoneDefaultConstructor<T>
extends ObjectReaderAdapter<T> {
final String[] paramNames;
final FieldReader[] setterFieldReaders;
private final Function<Map<Long, Object>, T> creator;
final Function<Map<Long, Object>, T> creatorFunction;
final Map<Long, FieldReader> paramFieldReaderMap;
final Constructor noneDefaultConstructor;
public ObjectReaderNoneDefaultConstructor(
Class objectClass,
@ -47,12 +49,17 @@ public class ObjectReaderNoneDefaultConstructor<T>
);
this.paramNames = paramNames;
this.creator = creator;
this.creatorFunction = creator;
this.setterFieldReaders = setterFieldReaders;
this.paramFieldReaderMap = new HashMap<>();
for (FieldReader paramFieldReader : paramFieldReaders) {
paramFieldReaderMap.put(paramFieldReader.fieldNameHash, paramFieldReader);
}
if (creatorFunction instanceof ConstructorFunction) {
noneDefaultConstructor = ((ConstructorFunction) creator).constructor;
} else {
noneDefaultConstructor = null;
}
}
static FieldReader[] concat(FieldReader[] a, FieldReader[] b) {
@ -65,9 +72,14 @@ public class ObjectReaderNoneDefaultConstructor<T>
return a;
}
@SuppressWarnings("rawtypes")
public Collection<FieldReader> getParameterFieldReaders() {
return paramFieldReaderMap.values();
}
@Override
public T createInstanceNoneDefaultConstructor(Map<Long, Object> values) {
return creator.apply(values);
return creatorFunction.apply(values);
}
@Override
@ -342,7 +354,7 @@ public class ObjectReaderNoneDefaultConstructor<T>
}
Map<Long, Object> argsMap = valueMap == null ? Collections.emptyMap() : valueMap;
T object = creator.apply(argsMap);
T object = creatorFunction.apply(argsMap);
if (setterFieldReaders != null && valueMap != null) {
for (int i = 0; i < setterFieldReaders.length; i++) {
@ -521,4 +533,13 @@ public class ObjectReaderNoneDefaultConstructor<T>
return object;
}
public T createInstance(Object[] args) {
try {
return (T) noneDefaultConstructor.newInstance(args);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
throw new JSONException("invoke constructor error, " + constructor, e);
}
}
}

View File

@ -1356,18 +1356,50 @@ public class IOUtils {
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) == ALSE;
}
public static boolean notALSE(byte[] buf, int pos) {
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) != ALSE;
}
public static boolean isALSE(char[] buf, int pos) {
return getLongUnaligned(buf, pos) == ALSE_64;
}
public static boolean notALSE(char[] buf, int pos) {
return getLongUnaligned(buf, pos) != ALSE_64;
}
public static boolean isNULL(byte[] buf, int pos) {
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) == NULL_32;
}
public static boolean notNULL(byte[] buf, int pos) {
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) != NULL_32;
}
public static boolean isTRUE(byte[] buf, int pos) {
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) == TRUE;
}
public static boolean notTRUE(byte[] buf, int pos) {
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) != TRUE;
}
public static boolean isTRUE(char[] buf, int pos) {
return UNSAFE.getLong(buf, ARRAY_BYTE_BASE_OFFSET + ((long) pos << 1)) == TRUE_64;
}
public static boolean notTRUE(char[] buf, int pos) {
return UNSAFE.getLong(buf, ARRAY_BYTE_BASE_OFFSET + ((long) pos << 1)) != TRUE_64;
}
public static boolean isNULL(char[] buf, int pos) {
return getLongUnaligned(buf, pos) == NULL_64;
}
public static boolean notNULL(char[] buf, int pos) {
return getLongUnaligned(buf, pos) != NULL_64;
}
public static void putNULL(byte[] buf, int pos) {
UNSAFE.putInt(buf, ARRAY_CHAR_BASE_OFFSET + pos, NULL_32);
}

View File

@ -1019,6 +1019,15 @@
<module>test-jdk17</module>
</modules>
</profile>
<profile>
<id>enable-for-jdk21+</id>
<activation>
<jdk>[21,)</jdk>
</activation>
<modules>
<module>benchmark_21</module>
</modules>
</profile>
<profile>
<id>enable-codegen</id>
<activation>

View File

@ -28,5 +28,9 @@
<artifactId>fastjson2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -2,6 +2,10 @@ package com.alibaba.fastjson2;
import org.junit.jupiter.api.Test;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class RecordTest {
@ -14,7 +18,49 @@ public class RecordTest {
JSONObject object = JSON.parseObject(str);
assertEquals(item.value, object.get("value"));
Item item2 = JSON.parseObject(str, Item.class);
assertEquals(item.value, item2.value);
}
record Item(int value) { }
public record Item(int value) {
}
@Test
public void test1() {
Item1 item = new Item1(Arrays.asList("abc"));
String str = JSON.toJSONString(item);
Item1 item2 = JSON.parseObject(str, Item1.class);
assertEquals(item.value, item2.value);
}
public record Item1(List<String> value) {
}
@Test
public void test2() {
Item2 item = new Item2(Arrays.asList(new Item(123)));
String str = JSON.toJSONString(item);
Item2 item2 = JSON.parseObject(str, Item2.class);
assertEquals(item.value.size(), item2.value.size());
assertEquals(item.value.get(0).value, item2.value.get(0).value);
}
public record Item2(List<Item> value) {
}
@Test
public void test3() {
Item3 item = new Item3(new User(true, "abc"));
String str = JSON.toJSONString(item);
Item3 item2 = JSON.parseObject(str, Item3.class);
assertEquals(item.user.default_profile, item2.user.default_profile);
assertEquals(item.user.screen_name, item2.user.screen_name);
}
public record User(boolean default_profile, String screen_name) {
}
public record Item3(User user) implements Serializable {
}
}