();
final Reader r;
private char[] buf;
private int end = -1;
private int readCount = 0;
ReaderValidator(Reader r) {
this.r = r;
buf = bufLocal.get();
if (buf != null) {
bufLocal.set(null);
} else {
buf = new char[1024 * 8];
}
next();
skipWhiteSpace();
}
void next() {
if (pos < end) {
ch = buf[++pos];
} else {
if (!eof) {
int len;
try {
len = r.read(buf, 0, buf.length);
readCount++;
} catch (IOException ex) {
throw new JSONException("read error");
}
if (len > 0) {
ch = buf[0];
pos = 0;
end = len - 1;
}
else if (len == -1) {
pos = 0;
end = 0;
buf = null;
ch = '\0';
eof = true;
} else {
pos = 0;
end = 0;
buf = null;
ch = '\0';
eof = true;
throw new JSONException("read error");
}
}
}
}
public void close() throws IOException {
bufLocal.set(buf);
r.close();
}
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/JSONWriter.java
================================================
package com.alibaba.fastjson;
import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
import java.io.Writer;
import static com.alibaba.fastjson.JSONStreamContext.*;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class JSONWriter implements Closeable, Flushable {
private SerializeWriter writer;
private JSONSerializer serializer;
private JSONStreamContext context;
public JSONWriter(Writer out){
writer = new SerializeWriter(out);
serializer = new JSONSerializer(writer);
}
public void config(SerializerFeature feature, boolean state) {
this.writer.config(feature, state);
}
public void startObject() {
if (context != null) {
beginStructure();
}
context = new JSONStreamContext(context, JSONStreamContext.StartObject);
writer.write('{');
}
public void endObject() {
writer.write('}');
endStructure();
}
public void writeKey(String key) {
writeObject(key);
}
public void writeValue(Object object) {
writeObject(object);
}
public void writeObject(String object) {
beforeWrite();
serializer.write(object);
afterWrite();
}
public void writeObject(Object object) {
beforeWrite();
serializer.write(object);
afterWrite();
}
public void startArray() {
if (context != null) {
beginStructure();
}
context = new JSONStreamContext(context, StartArray);
writer.write('[');
}
private void beginStructure() {
final int state = context.state;
switch (context.state) {
case PropertyKey:
writer.write(':');
break;
case ArrayValue:
writer.write(',');
break;
case StartObject:
break;
case StartArray:
break;
default:
throw new JSONException("illegal state : " + state);
}
}
public void endArray() {
writer.write(']');
endStructure();
}
private void endStructure() {
context = context.parent;
if (context == null) {
return;
}
int newState = -1;
switch (context.state) {
case PropertyKey:
newState = PropertyValue;
break;
case StartArray:
newState = ArrayValue;
break;
case ArrayValue:
break;
case StartObject:
newState = PropertyKey;
break;
default:
break;
}
if (newState != -1) {
context.state = newState;
}
}
private void beforeWrite() {
if (context == null) {
return;
}
switch (context.state) {
case StartObject:
case StartArray:
break;
case PropertyKey:
writer.write(':');
break;
case PropertyValue:
writer.write(',');
break;
case ArrayValue:
writer.write(',');
break;
default:
break;
}
}
private void afterWrite() {
if (context == null) {
return;
}
final int state = context.state;
int newState = -1;
switch (state) {
case PropertyKey:
newState = PropertyValue;
break;
case StartObject:
case PropertyValue:
newState = PropertyKey;
break;
case StartArray:
newState = ArrayValue;
break;
case ArrayValue:
break;
default:
break;
}
if (newState != -1) {
context.state = newState;
}
}
public void flush() throws IOException {
writer.flush();
}
public void close() throws IOException {
writer.close();
}
@Deprecated
public void writeStartObject() {
startObject();
}
@Deprecated
public void writeEndObject() {
endObject();
}
@Deprecated
public void writeStartArray() {
startArray();
}
@Deprecated
public void writeEndArray() {
endArray();
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/PropertyNamingStrategy.java
================================================
package com.alibaba.fastjson;
/**
* @since 1.2.15
*/
public enum PropertyNamingStrategy {
CamelCase, // camelCase
PascalCase, // PascalCase
SnakeCase, // snake_case
KebabCase, // kebab-case
NoChange, //
NeverUseThisValueExceptDefaultValue;
public String translate(String propertyName) {
switch (this) {
case SnakeCase: {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < propertyName.length(); ++i) {
char ch = propertyName.charAt(i);
if (ch >= 'A' && ch <= 'Z') {
char ch_ucase = (char) (ch + 32);
if (i > 0) {
buf.append('_');
}
buf.append(ch_ucase);
} else {
buf.append(ch);
}
}
return buf.toString();
}
case KebabCase: {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < propertyName.length(); ++i) {
char ch = propertyName.charAt(i);
if (ch >= 'A' && ch <= 'Z') {
char ch_ucase = (char) (ch + 32);
if (i > 0) {
buf.append('-');
}
buf.append(ch_ucase);
} else {
buf.append(ch);
}
}
return buf.toString();
}
case PascalCase: {
char ch = propertyName.charAt(0);
if (ch >= 'a' && ch <= 'z') {
char[] chars = propertyName.toCharArray();
chars[0] -= 32;
return new String(chars);
}
return propertyName;
}
case CamelCase: {
char ch = propertyName.charAt(0);
if (ch >= 'A' && ch <= 'Z') {
char[] chars = propertyName.toCharArray();
chars[0] += 32;
return new String(chars);
}
return propertyName;
}
case NoChange:
case NeverUseThisValueExceptDefaultValue:
default:
return propertyName;
}
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/TypeReference.java
================================================
package com.alibaba.fastjson;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.alibaba.fastjson.util.ParameterizedTypeImpl;
import com.alibaba.fastjson.util.TypeUtils;
/**
* Represents a generic type {@code T}. Java doesn't yet provide a way to
* represent generic types, so this class does. Forces clients to create a
* subclass of this class which enables retrieval the type information even at
* runtime.
*
* For example, to create a type literal for {@code List}, you can
* create an empty anonymous inner class:
*
*
* TypeReference<List<String>> list = new TypeReference<List<String>>() {};
*
* This syntax cannot be used to create type literals that have wildcard
* parameters, such as {@code Class>} or {@code List extends CharSequence>}.
*/
public class TypeReference {
static ConcurrentMap classTypeCache
= new ConcurrentHashMap(16, 0.75f, 1);
protected final Type type;
/**
* Constructs a new type literal. Derives represented class from type
* parameter.
*
* Clients create an empty anonymous subclass. Doing so embeds the type
* parameter in the anonymous class's type hierarchy so we can reconstitute it
* at runtime despite erasure.
*/
protected TypeReference(){
Type superClass = getClass().getGenericSuperclass();
Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
Type cachedType = classTypeCache.get(type);
if (cachedType == null) {
classTypeCache.putIfAbsent(type, type);
cachedType = classTypeCache.get(type);
}
this.type = cachedType;
}
/**
* @since 1.2.9
* @param actualTypeArguments
*/
protected TypeReference(Type... actualTypeArguments){
Class> thisClass = this.getClass();
Type superClass = thisClass.getGenericSuperclass();
ParameterizedType argType = (ParameterizedType) ((ParameterizedType) superClass).getActualTypeArguments()[0];
Type rawType = argType.getRawType();
Type[] argTypes = argType.getActualTypeArguments();
int actualIndex = 0;
for (int i = 0; i < argTypes.length; ++i) {
if (argTypes[i] instanceof TypeVariable &&
actualIndex < actualTypeArguments.length) {
argTypes[i] = actualTypeArguments[actualIndex++];
}
// fix for openjdk and android env
if (argTypes[i] instanceof GenericArrayType) {
argTypes[i] = TypeUtils.checkPrimitiveArray(
(GenericArrayType) argTypes[i]);
}
// 如果有多层泛型且该泛型已经注明实现的情况下,判断该泛型下一层是否还有泛型
if(argTypes[i] instanceof ParameterizedType) {
argTypes[i] = handlerParameterizedType((ParameterizedType) argTypes[i], actualTypeArguments, actualIndex);
}
}
Type key = new ParameterizedTypeImpl(argTypes, thisClass, rawType);
Type cachedType = classTypeCache.get(key);
if (cachedType == null) {
classTypeCache.putIfAbsent(key, key);
cachedType = classTypeCache.get(key);
}
type = cachedType;
}
public static Type intern(ParameterizedTypeImpl type) {
Type cachedType = classTypeCache.get(type);
if (cachedType == null) {
classTypeCache.putIfAbsent(type, type);
cachedType = classTypeCache.get(type);
}
return cachedType;
}
private Type handlerParameterizedType(ParameterizedType type, Type[] actualTypeArguments, int actualIndex) {
Class> thisClass = this.getClass();
Type rawType = type.getRawType();
Type[] argTypes = type.getActualTypeArguments();
for(int i = 0; i < argTypes.length; ++i) {
if (argTypes[i] instanceof TypeVariable && actualIndex < actualTypeArguments.length) {
argTypes[i] = actualTypeArguments[actualIndex++];
}
// fix for openjdk and android env
if (argTypes[i] instanceof GenericArrayType) {
argTypes[i] = TypeUtils.checkPrimitiveArray(
(GenericArrayType) argTypes[i]);
}
// 如果有多层泛型且该泛型已经注明实现的情况下,判断该泛型下一层是否还有泛型
if(argTypes[i] instanceof ParameterizedType) {
argTypes[i] = handlerParameterizedType((ParameterizedType) argTypes[i], actualTypeArguments, actualIndex);
}
}
Type key = new ParameterizedTypeImpl(argTypes, thisClass, rawType);
return key;
}
/**
* Gets underlying {@code Type} instance.
*/
public Type getType() {
return type;
}
public final static Type LIST_STRING = new TypeReference>() {}.getType();
}
================================================
FILE: src/main/java/com/alibaba/fastjson/annotation/JSONCreator.java
================================================
package com.alibaba.fastjson.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD })
public @interface JSONCreator {
}
================================================
FILE: src/main/java/com/alibaba/fastjson/annotation/JSONField.java
================================================
/*
* Copyright 1999-2017 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.fastjson.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
/**
* @author wenshao[szujobs@hotmail.com]
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
public @interface JSONField {
/**
* config encode/decode ordinal
* @since 1.1.42
* @return
*/
int ordinal() default 0;
String name() default "";
String format() default "";
boolean serialize() default true;
boolean deserialize() default true;
SerializerFeature[] serialzeFeatures() default {};
Feature[] parseFeatures() default {};
String label() default "";
/**
* @since 1.2.12
*/
boolean jsonDirect() default false;
/**
* Serializer class to use for serializing associated value.
*
* @since 1.2.16
*/
Class> serializeUsing() default Void.class;
/**
* Deserializer class to use for deserializing associated value.
*
* @since 1.2.16
*/
Class> deserializeUsing() default Void.class;
/**
* @since 1.2.21
* @return the alternative names of the field when it is deserialized
*/
String[] alternateNames() default {};
/**
* @since 1.2.31
*/
boolean unwrapped() default false;
/**
* Only support Object
*
* @since 1.2.61
*/
String defaultValue() default "";
}
================================================
FILE: src/main/java/com/alibaba/fastjson/annotation/JSONPOJOBuilder.java
================================================
package com.alibaba.fastjson.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @since 1.2.8
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface JSONPOJOBuilder {
/**
* Property to use for re-defining which zero-argument method
* is considered the actual "build-method": method called after
* all data has been bound, and the actual instance needs to
* be instantiated.
*
* Default value is "build".
*/
String buildMethod() default "build";
/**
* Property used for (re)defining name prefix to use for
* auto-detecting "with-methods": methods that are similar to
* "set-methods" (in that they take an argument), but that
* may also return the new builder instance to use
* (which may be 'this', or a new modified builder instance).
* Note that in addition to this prefix, it is also possible
* to use {@link com.alibaba.fastjson.annotation.JSONField}
* annotation to indicate "with-methods".
*
* Default value is "with", so that method named "withValue()"
* would be used for binding JSON property "value" (using type
* indicated by the argument; or one defined with annotations.
*/
String withPrefix() default "with";
}
================================================
FILE: src/main/java/com/alibaba/fastjson/annotation/JSONType.java
================================================
package com.alibaba.fastjson.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.serializer.SerializerFeature;
/**
* @author wenshao[szujobs@hotmail.com]
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface JSONType {
boolean asm() default true;
String[] orders() default {};
/**
* @since 1.2.6
*/
String[] includes() default {};
String[] ignores() default {};
SerializerFeature[] serialzeFeatures() default {};
Feature[] parseFeatures() default {};
boolean alphabetic() default true;
Class> mappingTo() default Void.class;
Class> builder() default Void.class;
/**
* @since 1.2.11
*/
String typeName() default "";
/**
* @since 1.2.32
*/
String typeKey() default "";
/**
* @since 1.2.11
*/
Class>[] seeAlso() default{};
/**
* @since 1.2.14
*/
Class> serializer() default Void.class;
/**
* @since 1.2.14
*/
Class> deserializer() default Void.class;
boolean serializeEnumAsJavaBean() default false;
PropertyNamingStrategy naming() default PropertyNamingStrategy.NeverUseThisValueExceptDefaultValue;
/**
* @since 1.2.49
*/
Class extends SerializeFilter>[] serialzeFilters() default {};
/**
* @since 1.2.71
* @return
*/
Class extends ParserConfig.AutoTypeCheckHandler> autoTypeCheckHandler() default ParserConfig.AutoTypeCheckHandler.class;
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/ByteVector.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2007 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.alibaba.fastjson.asm;
/**
* A dynamically extensible vector of bytes. This class is roughly equivalent to
* a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient.
*
* @author Eric Bruneton
*/
public class ByteVector {
/**
* The content of this vector.
*/
public byte[] data;
/**
* Actual number of bytes in this vector.
*/
public int length;
/**
* Constructs a new {@link ByteVector ByteVector} with a default initial size.
*/
public ByteVector() {
data = new byte[64];
}
/**
* Constructs a new {@link ByteVector ByteVector} with the given initial size.
*
* @param initialSize the initial size of the byte vector to be constructed.
*/
public ByteVector(final int initialSize) {
data = new byte[initialSize];
}
/**
* Puts a byte into this byte vector. The byte vector is automatically enlarged
* if necessary.
*
* @param b a byte.
* @return this byte vector.
*/
public ByteVector putByte(final int b) {
int length = this.length;
if (length + 1 > data.length) {
enlarge(1);
}
data[length++] = (byte) b;
this.length = length;
return this;
}
/**
* Puts two bytes into this byte vector. The byte vector is automatically
* enlarged if necessary.
*
* @param b1 a byte.
* @param b2 another byte.
* @return this byte vector.
*/
ByteVector put11(final int b1, final int b2) {
int length = this.length;
if (length + 2 > data.length) {
enlarge(2);
}
final byte[] data = this.data;
data[length++] = (byte) b1;
data[length++] = (byte) b2;
this.length = length;
return this;
}
/**
* Puts a short into this byte vector. The byte vector is automatically enlarged
* if necessary.
*
* @param s a short.
* @return this byte vector.
*/
public ByteVector putShort(final int s) {
int length = this.length;
if (length + 2 > data.length) {
enlarge(2);
}
final byte[] data = this.data;
data[length++] = (byte) (s >>> 8);
data[length++] = (byte) s;
this.length = length;
return this;
}
/**
* Puts a byte and a short into this byte vector. The byte vector is
* automatically enlarged if necessary.
*
* @param b a byte.
* @param s a short.
* @return this byte vector.
*/
public ByteVector put12(final int b, final int s) {
int length = this.length;
if (length + 3 > data.length) {
enlarge(3);
}
final byte[] data = this.data;
data[length++] = (byte) b;
data[length++] = (byte) (s >>> 8);
data[length++] = (byte) s;
this.length = length;
return this;
}
/**
* Puts an int into this byte vector. The byte vector is automatically enlarged
* if necessary.
*
* @param i an int.
* @return this byte vector.
*/
public ByteVector putInt(final int i) {
int length = this.length;
if (length + 4 > data.length) {
enlarge(4);
}
final byte[] data = this.data;
data[length++] = (byte) (i >>> 24);
data[length++] = (byte) (i >>> 16);
data[length++] = (byte) (i >>> 8);
data[length++] = (byte) i;
this.length = length;
return this;
}
/**
* Puts an UTF8 string into this byte vector. The byte vector is automatically
* enlarged if necessary.
*
* @param s a String.
* @return this byte vector.
*/
public ByteVector putUTF8(final String s) {
final int charLength = s.length();
int len = length;
if (len + 2 + charLength > data.length) {
enlarge(2 + charLength);
}
final byte[] data = this.data;
// optimistic algorithm: instead of computing the byte length and then
// serializing the string (which requires two loops), we assume the byte
// length is equal to char length (which is the most frequent case), and
// we start serializing the string right away. During the serialization,
// if we find that this assumption is wrong, we continue with the
// general method.
data[len++] = (byte) (charLength >>> 8);
data[len++] = (byte) charLength;
for (int i = 0; i < charLength; ++i) {
final char c = s.charAt(i);
if ((c >= '\001' && c <= '\177') || (c >= '\u4E00' && c <= '\u9FFF')) {
data[len++] = (byte) c;
} else {
throw new UnsupportedOperationException();
}
}
length = len;
return this;
}
/**
* Puts an array of bytes into this byte vector. The byte vector is
* automatically enlarged if necessary.
*
* @param b an array of bytes. May be null to put len null
* bytes into this byte vector.
* @param off index of the fist byte of b that must be copied.
* @param len number of bytes of b that must be copied.
* @return this byte vector.
*/
public ByteVector putByteArray(final byte[] b, final int off, final int len) {
if (length + len > data.length) {
enlarge(len);
}
if (b != null) {
System.arraycopy(b, off, data, length, len);
}
length += len;
return this;
}
/**
* Enlarge this byte vector so that it can receive n more bytes.
*
* @param size number of additional bytes that this byte vector should be able
* to receive.
*/
private void enlarge(final int size) {
final int length1 = 2 * data.length;
final int length2 = length + size;
final byte[] newData = new byte[length1 > length2 ? length1 : length2];
System.arraycopy(data, 0, newData, 0, length);
data = newData;
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/ClassReader.java
================================================
package com.alibaba.fastjson.asm;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by wenshao on 05/08/2017.
*/
public class ClassReader {
public final byte[] b;
private final int[] items;
private final String[] strings;
private final int maxStringLength;
public final int header;
private boolean readAnnotations;
public ClassReader(InputStream is, boolean readAnnotations) throws IOException {
this.readAnnotations = readAnnotations;
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
for (; ; ) {
int len = is.read(buf);
if (len == -1) {
break;
}
if (len > 0) {
out.write(buf, 0, len);
}
}
is.close();
this.b = out.toByteArray();
}
// parses the constant pool
items = new int[readUnsignedShort(8)];
int n = items.length;
strings = new String[n];
int max = 0;
int index = 10;
for (int i = 1; i < n; ++i) {
items[i] = index + 1;
int size;
switch (b[index]) {
case 9: // FIELD:
case 10: // METH:
case 11: //IMETH:
case 3: //INT:
case 4: //FLOAT:
case 18: //INVOKEDYN:
case 12: //NAME_TYPE:
size = 5;
break;
case 5: //LONG:
case 6: //DOUBLE:
size = 9;
++i;
break;
case 15: //MHANDLE:
size = 4;
break;
case 1: //UTF8:
size = 3 + readUnsignedShort(index + 1);
if (size > max) {
max = size;
}
break;
// case HamConstants.CLASS:
// case HamConstants.STR:
default:
size = 3;
break;
}
index += size;
}
maxStringLength = max;
// the class header information starts just after the constant pool
header = index;
}
public void accept(final TypeCollector classVisitor) {
char[] c = new char[maxStringLength]; // buffer used to read strings
int i, j; // loop variables
int u, v; // indexes in b
int anns = 0;
//read annotations
if (readAnnotations) {
u = getAttributes();
for (i = readUnsignedShort(u); i > 0; --i) {
String attrName = readUTF8(u + 2, c);
if ("RuntimeVisibleAnnotations".equals(attrName)) {
anns = u + 8;
break;
}
u += 6 + readInt(u + 4);
}
}
// visits the header
u = header;
int len = readUnsignedShort(u + 6);
u += 8;
for (i = 0; i < len; ++i) {
u += 2;
}
v = u;
i = readUnsignedShort(v);
v += 2;
for (; i > 0; --i) {
j = readUnsignedShort(v + 6);
v += 8;
for (; j > 0; --j) {
v += 6 + readInt(v + 2);
}
}
i = readUnsignedShort(v);
v += 2;
for (; i > 0; --i) {
j = readUnsignedShort(v + 6);
v += 8;
for (; j > 0; --j) {
v += 6 + readInt(v + 2);
}
}
i = readUnsignedShort(v);
v += 2;
for (; i > 0; --i) {
v += 6 + readInt(v + 2);
}
if (anns != 0) {
for (i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
String name = readUTF8(v, c);
classVisitor.visitAnnotation(name);
}
}
// visits the fields
i = readUnsignedShort(u);
u += 2;
for (; i > 0; --i) {
j = readUnsignedShort(u + 6);
u += 8;
for (; j > 0; --j) {
u += 6 + readInt(u + 2);
}
}
// visits the methods
i = readUnsignedShort(u);
u += 2;
for (; i > 0; --i) {
// inlined in original ASM source, now a method call
u = readMethod(classVisitor, c, u);
}
}
private int getAttributes() {
// skips the header
int u = header + 8 + readUnsignedShort(header + 6) * 2;
// skips fields and methods
for (int i = readUnsignedShort(u); i > 0; --i) {
for (int j = readUnsignedShort(u + 8); j > 0; --j) {
u += 6 + readInt(u + 12);
}
u += 8;
}
u += 2;
for (int i = readUnsignedShort(u); i > 0; --i) {
for (int j = readUnsignedShort(u + 8); j > 0; --j) {
u += 6 + readInt(u + 12);
}
u += 8;
}
// the attribute_info structure starts just after the methods
return u + 2;
}
private int readMethod(TypeCollector classVisitor, char[] c, int u) {
int v;
int w;
int j;
String attrName;
int k;
int access = readUnsignedShort(u);
String name = readUTF8(u + 2, c);
String desc = readUTF8(u + 4, c);
v = 0;
w = 0;
// looks for Code and Exceptions attributes
j = readUnsignedShort(u + 6);
u += 8;
for (; j > 0; --j) {
attrName = readUTF8(u, c);
int attrSize = readInt(u + 2);
u += 6;
// tests are sorted in decreasing frequency order
// (based on frequencies observed on typical classes)
if (attrName.equals("Code")) {
v = u;
}
u += attrSize;
}
// reads declared exceptions
if (w == 0) {
} else {
w += 2;
for (j = 0; j < readUnsignedShort(w); ++j) {
w += 2;
}
}
// visits the method's code, if any
MethodCollector mv = classVisitor.visitMethod(access, name, desc);
if (mv != null && v != 0) {
int codeLength = readInt(v + 4);
v += 8;
int codeStart = v;
int codeEnd = v + codeLength;
v = codeEnd;
j = readUnsignedShort(v);
v += 2;
for (; j > 0; --j) {
v += 8;
}
// parses the local variable, line number tables, and code
// attributes
int varTable = 0;
int varTypeTable = 0;
j = readUnsignedShort(v);
v += 2;
for (; j > 0; --j) {
attrName = readUTF8(v, c);
if (attrName.equals("LocalVariableTable")) {
varTable = v + 6;
} else if (attrName.equals("LocalVariableTypeTable")) {
varTypeTable = v + 6;
}
v += 6 + readInt(v + 2);
}
v = codeStart;
// visits the local variable tables
if (varTable != 0) {
if (varTypeTable != 0) {
k = readUnsignedShort(varTypeTable) * 3;
w = varTypeTable + 2;
int[] typeTable = new int[k];
while (k > 0) {
typeTable[--k] = w + 6; // signature
typeTable[--k] = readUnsignedShort(w + 8); // index
typeTable[--k] = readUnsignedShort(w); // start
w += 10;
}
}
k = readUnsignedShort(varTable);
w = varTable + 2;
for (; k > 0; --k) {
int index = readUnsignedShort(w + 8);
mv.visitLocalVariable(readUTF8(w + 4, c), index);
w += 10;
}
}
}
return u;
}
private int readUnsignedShort(final int index) {
byte[] b = this.b;
return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
}
private int readInt(final int index) {
byte[] b = this.b;
return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
| ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
}
private String readUTF8(int index, final char[] buf) {
int item = readUnsignedShort(index);
String s = strings[item];
if (s != null) {
return s;
}
index = items[item];
return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf);
}
private String readUTF(int index, final int utfLen, final char[] buf) {
int endIndex = index + utfLen;
byte[] b = this.b;
int strLen = 0;
int c;
int st = 0;
char cc = 0;
while (index < endIndex) {
c = b[index++];
switch (st) {
case 0:
c = c & 0xFF;
if (c < 0x80) { // 0xxxxxxx
buf[strLen++] = (char) c;
} else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx
cc = (char) (c & 0x1F);
st = 1;
} else { // 1110 xxxx 10xx xxxx 10xx xxxx
cc = (char) (c & 0x0F);
st = 2;
}
break;
case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char
buf[strLen++] = (char) ((cc << 6) | (c & 0x3F));
st = 0;
break;
case 2: // byte 2 of 3-byte char
cc = (char) ((cc << 6) | (c & 0x3F));
st = 1;
break;
}
}
return new String(buf, 0, strLen);
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/ClassWriter.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2007 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.alibaba.fastjson.asm;
/**
*
* @author Eric Bruneton
*/
public class ClassWriter {
/**
* Minor and major version numbers of the class to be generated.
*/
int version;
/**
* Index of the next item to be added in the constant pool.
*/
int index;
/**
* The constant pool of this class.
*/
final ByteVector pool;
/**
* The constant pool's hash table data.
*/
Item[] items;
/**
* The threshold of the constant pool's hash table.
*/
int threshold;
/**
* A reusable key used to look for items in the {@link #items} hash table.
*/
final Item key;
/**
* A reusable key used to look for items in the {@link #items} hash table.
*/
final Item key2;
/**
* A reusable key used to look for items in the {@link #items} hash table.
*/
final Item key3;
/**
* A type table used to temporarily store internal names that will not necessarily be stored in the constant pool.
* This type table is used by the control flow and data flow analysis algorithm used to compute stack map frames
* from scratch. This array associates to each index i the Item whose index is i . All Item objects
* stored in this array are also stored in the {@link #items} hash table. These two arrays allow to retrieve an Item
* from its index or, conversely, to get the index of an Item from its value. Each Item stores an internal name in
* its {@link Item#strVal1} field.
*/
Item[] typeTable;
/**
* The access flags of this class.
*/
private int access;
/**
* The constant pool item that contains the internal name of this class.
*/
private int name;
/**
* The internal name of this class.
*/
String thisName;
/**
* The constant pool item that contains the internal name of the super class of this class.
*/
private int superName;
/**
* Number of interfaces implemented or extended by this class or interface.
*/
private int interfaceCount;
/**
* The interfaces implemented or extended by this class or interface. More precisely, this array contains the
* indexes of the constant pool items that contain the internal names of these interfaces.
*/
private int[] interfaces;
/**
* The fields of this class. These fields are stored in a linked list of {@link FieldWriter} objects, linked to each
* other by their {@link FieldWriter#next} field. This field stores the first element of this list.
*/
FieldWriter firstField;
/**
* The fields of this class. These fields are stored in a linked list of {@link FieldWriter} objects, linked to each
* other by their {@link FieldWriter#next} field. This field stores the last element of this list.
*/
FieldWriter lastField;
/**
* The methods of this class. These methods are stored in a linked list of {@link MethodWriter} objects, linked to
* each other by their {@link MethodWriter#next} field. This field stores the first element of this list.
*/
MethodWriter firstMethod;
/**
* The methods of this class. These methods are stored in a linked list of {@link MethodWriter} objects, linked to
* each other by their {@link MethodWriter#next} field. This field stores the last element of this list.
*/
MethodWriter lastMethod;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
public ClassWriter(){
this(0);
}
private ClassWriter(final int flags){
index = 1;
pool = new ByteVector();
items = new Item[256];
threshold = (int) (0.75d * items.length);
key = new Item();
key2 = new Item();
key3 = new Item();
}
// ------------------------------------------------------------------------
// Implementation of the ClassVisitor interface
// ------------------------------------------------------------------------
public void visit(final int version, final int access, final String name, final String superName, final String[] interfaces) {
this.version = version;
this.access = access;
this.name = newClassItem(name).index;
thisName = name;
this.superName = superName == null ? 0 : newClassItem(superName).index;
if (interfaces != null && interfaces.length > 0) {
interfaceCount = interfaces.length;
this.interfaces = new int[interfaceCount];
for (int i = 0; i < interfaceCount; ++i) {
this.interfaces[i] = newClassItem(interfaces[i]).index;
}
}
}
// ------------------------------------------------------------------------
// Other public methods
// ------------------------------------------------------------------------
/**
* Returns the bytecode of the class that was build with this class writer.
*
* @return the bytecode of the class that was build with this class writer.
*/
public byte[] toByteArray() {
// computes the real size of the bytecode of this class
int size = 24 + 2 * interfaceCount;
int nbFields = 0;
FieldWriter fb = firstField;
while (fb != null) {
++nbFields;
size += fb.getSize();
fb = fb.next;
}
int nbMethods = 0;
MethodWriter mb = firstMethod;
while (mb != null) {
++nbMethods;
size += mb.getSize();
mb = mb.next;
}
int attributeCount = 0;
size += pool.length;
// allocates a byte vector of this size, in order to avoid unnecessary
// arraycopy operations in the ByteVector.enlarge() method
ByteVector out = new ByteVector(size);
out.putInt(0xCAFEBABE).putInt(version);
out.putShort(index).putByteArray(pool.data, 0, pool.length);
int mask = 393216; // Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / (ClassWriter.ACC_SYNTHETIC_ATTRIBUTE / Opcodes.ACC_SYNTHETIC));
out.putShort(access & ~mask).putShort(name).putShort(superName);
out.putShort(interfaceCount);
for (int i = 0; i < interfaceCount; ++i) {
out.putShort(interfaces[i]);
}
out.putShort(nbFields);
fb = firstField;
while (fb != null) {
fb.put(out);
fb = fb.next;
}
out.putShort(nbMethods);
mb = firstMethod;
while (mb != null) {
mb.put(out);
mb = mb.next;
}
out.putShort(attributeCount);
return out.data;
}
// ------------------------------------------------------------------------
// Utility methods: constant pool management
// ------------------------------------------------------------------------
/**
* Adds a number or string constant to the constant pool of the class being build. Does nothing if the constant pool
* already contains a similar item.
*
* @param cst the value of the constant to be added to the constant pool. This parameter must be an {@link Integer},
* a {@link Float}, a {@link Long}, a {@link Double}, a {@link String} or a {@link Type}.
* @return a new or already existing constant item with the given value.
*/
Item newConstItem(final Object cst) {
if (cst instanceof Integer) {
int val = ((Integer) cst).intValue();
// return newInteger(val);
key.set(val);
Item result = get(key);
if (result == null) {
pool.putByte(3 /* INT */ ).putInt(val);
result = new Item(index++, key);
put(result);
}
return result;
} else if (cst instanceof String) {
return newString((String) cst);
} else if (cst instanceof Type) {
Type t = (Type) cst;
return newClassItem(t.sort == 10 /*Type.OBJECT*/ ? t.getInternalName() : t.getDescriptor());
} else {
throw new IllegalArgumentException("value " + cst);
}
}
public int newUTF8(final String value) {
key.set(1 /* UTF8 */, value, null, null);
Item result = get(key);
if (result == null) {
pool.putByte(1 /* UTF8 */).putUTF8(value);
result = new Item(index++, key);
put(result);
}
return result.index;
}
public Item newClassItem(final String value) {
key2.set(7 /* CLASS */, value, null, null);
Item result = get(key2);
if (result == null) {
pool.put12(7 /* CLASS */, newUTF8(value));
result = new Item(index++, key2);
put(result);
}
return result;
}
/**
* Adds a field reference to the constant pool of the class being build. Does nothing if the constant pool already
* contains a similar item.
*
* @param owner the internal name of the field's owner class.
* @param name the field's name.
* @param desc the field's descriptor.
* @return a new or already existing field reference item.
*/
Item newFieldItem(final String owner, final String name, final String desc) {
key3.set(9 /* FIELD */, owner, name, desc);
Item result = get(key3);
if (result == null) {
// put122(9 /* FIELD */, newClassItem(owner).index, newNameTypeItem(name, desc).index);
int s1 = newClassItem(owner).index, s2 = newNameTypeItem(name, desc).index;
pool.put12(9 /* FIELD */, s1).putShort(s2);
result = new Item(index++, key3);
put(result);
}
return result;
}
/**
* Adds a method reference to the constant pool of the class being build. Does nothing if the constant pool already
* contains a similar item.
*
* @param owner the internal name of the method's owner class.
* @param name the method's name.
* @param desc the method's descriptor.
* @param itf true if owner is an interface.
* @return a new or already existing method reference item.
*/
Item newMethodItem(final String owner, final String name, final String desc, final boolean itf) {
int type = itf ? 11 /* IMETH */ : 10 /* METH */;
key3.set(type, owner, name, desc);
Item result = get(key3);
if (result == null) {
// put122(type, newClassItem(owner).index, newNameTypeItem(name, desc).index);
int s1 = newClassItem(owner).index, s2 = newNameTypeItem(name, desc).index;
pool.put12(type, s1).putShort(s2);
result = new Item(index++, key3);
put(result);
}
return result;
}
private Item newString(final String value) {
key2.set(8 /* STR */, value, null, null);
Item result = get(key2);
if (result == null) {
pool.put12(8 /*STR*/, newUTF8(value));
result = new Item(index++, key2);
put(result);
}
return result;
}
public Item newNameTypeItem(final String name, final String desc) {
key2.set(12 /* NAME_TYPE */, name, desc, null);
Item result = get(key2);
if (result == null) {
//put122(12 /* NAME_TYPE */, newUTF8(name), newUTF8(desc));
int s1 = newUTF8(name), s2 = newUTF8(desc);
pool.put12(12 /* NAME_TYPE */, s1).putShort(s2);
result = new Item(index++, key2);
put(result);
}
return result;
}
private Item get(final Item key) {
Item i = items[key.hashCode % items.length];
while (i != null && (i.type != key.type || !key.isEqualTo(i))) {
i = i.next;
}
return i;
}
private void put(final Item i) {
if (index > threshold) {
int ll = items.length;
int nl = ll * 2 + 1;
Item[] newItems = new Item[nl];
for (int l = ll - 1; l >= 0; --l) {
Item j = items[l];
while (j != null) {
int index = j.hashCode % newItems.length;
Item k = j.next;
j.next = newItems[index];
newItems[index] = j;
j = k;
}
}
items = newItems;
threshold = (int) (nl * 0.75);
}
int index = i.hashCode % items.length;
i.next = items[index];
items[index] = i;
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/FieldWriter.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2007 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.alibaba.fastjson.asm;
/**
* An FieldWriter that generates Java fields in bytecode form.
*
* @author Eric Bruneton
*/
public final class FieldWriter {
FieldWriter next;
/**
* Access flags of this field.
*/
private final int access;
/**
* The index of the constant pool item that contains the name of this method.
*/
private final int name;
/**
* The index of the constant pool item that contains the descriptor of this field.
*/
private final int desc;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
public FieldWriter(final ClassWriter cw, final int access, final String name, final String desc){
if (cw.firstField == null) {
cw.firstField = this;
} else {
cw.lastField.next = this;
}
cw.lastField = this;
this.access = access;
this.name = cw.newUTF8(name);
this.desc = cw.newUTF8(desc);
}
// ------------------------------------------------------------------------
// Implementation of the FieldVisitor interface
// ------------------------------------------------------------------------
public void visitEnd() {
}
// ------------------------------------------------------------------------
// Utility methods
// ------------------------------------------------------------------------
/**
* Returns the size of this field.
*
* @return the size of this field.
*/
int getSize() {
return 8;
}
/**
* Puts the content of this field into the given byte vector.
*
* @param out where the content of this field must be put.
*/
void put(final ByteVector out) {
final int mask = 393216; // Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / (ClassWriter.ACC_SYNTHETIC_ATTRIBUTE / Opcodes.ACC_SYNTHETIC));
out.putShort(access & ~mask).putShort(name).putShort(desc);
int attributeCount = 0;
out.putShort(attributeCount);
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/Item.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2007 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.alibaba.fastjson.asm;
/**
* A constant pool item. Constant pool items can be created with the 'newXXX' methods in the {@link ClassWriter} class.
*
* @author Eric Bruneton
*/
final class Item {
/**
* Index of this item in the constant pool.
*/
int index;
int type;
/**
* Value of this item, for an integer item.
*/
int intVal;
/**
* Value of this item, for a long item.
*/
long longVal;
/**
* First part of the value of this item, for items that do not hold a primitive value.
*/
String strVal1;
/**
* Second part of the value of this item, for items that do not hold a primitive value.
*/
String strVal2;
/**
* Third part of the value of this item, for items that do not hold a primitive value.
*/
String strVal3;
/**
* The hash code value of this constant pool item.
*/
int hashCode;
/**
* Link to another constant pool item, used for collision lists in the constant pool's hash table.
*/
Item next;
/**
* Constructs an uninitialized {@link Item}.
*/
Item(){
}
/**
* Constructs a copy of the given item.
*
* @param index index of the item to be constructed.
* @param i the item that must be copied into the item to be constructed.
*/
Item(final int index, final Item i){
this.index = index;
type = i.type;
intVal = i.intVal;
longVal = i.longVal;
strVal1 = i.strVal1;
strVal2 = i.strVal2;
strVal3 = i.strVal3;
hashCode = i.hashCode;
}
/**
* Sets this item to an item that do not hold a primitive value.
*
* @param type the type of this item.
* @param strVal1 first part of the value of this item.
* @param strVal2 second part of the value of this item.
* @param strVal3 third part of the value of this item.
*/
void set(final int type, final String strVal1, final String strVal2, final String strVal3) {
this.type = type;
this.strVal1 = strVal1;
this.strVal2 = strVal2;
this.strVal3 = strVal3;
switch (type) {
case 1 /* ClassWriter.UTF8 */:
case 8 /* ClassWriter.STR */:
case 7 /* ClassWriter.CLASS */:
case 13 /* ClassWriter.TYPE_NORMAL */:
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
return;
case 12 /* ClassWriter.NAME_TYPE */:
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() * strVal2.hashCode());
return;
// ClassWriter.FIELD:
// ClassWriter.METH:
// ClassWriter.IMETH:
default:
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() * strVal2.hashCode() * strVal3.hashCode());
}
}
/**
* Sets this item to an integer item.
*
* @param intVal the value of this item.
*/
void set(final int intVal) {
this.type = 3 /* ClassWriter.INT */;
this.intVal = intVal;
this.hashCode = 0x7FFFFFFF & (type + intVal);
}
/**
* Indicates if the given item is equal to this one. This method assumes that the two items have the same
* {@link #type} .
*
* @param i the item to be compared to this one. Both items must have the same {@link #type}.
* @return true if the given item if equal to this one, false otherwise.
*/
boolean isEqualTo(final Item i) {
switch (type) {
case 1 /* ClassWriter.UTF8 */:
case 8 /* ClassWriter.STR */:
case 7 /* ClassWriter.CLASS */ :
case 13 /* ClassWriter.TYPE_NORMAL */ :
return i.strVal1.equals(strVal1);
case 15 /* ClassWriter.TYPE_MERGED */ :
case 5 /* ClassWriter.LONG */ :
case 6 /* ClassWriter.DOUBLE */:
return i.longVal == longVal;
case 3 /* ClassWriter.INT */ :
case 4 /* ClassWriter.FLOAT */:
return i.intVal == intVal;
case 12 /* ClassWriter.NAME_TYPE */:
return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2);
// case ClassWriter.FIELD:
// case ClassWriter.METH:
// case ClassWriter.IMETH:
default:
return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2) && i.strVal3.equals(strVal3);
}
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/Label.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2007 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.alibaba.fastjson.asm;
/**
* A label represents a position in the bytecode of a method. Labels are used for jump, goto, and switch instructions,
* and for try catch blocks. A label designates the instruction that is just after. Note however that there can
* be other elements between a label and the instruction it designates (such as other labels, stack map frames, line
* numbers, etc.).
*
* @author Eric Bruneton
*/
public class Label {
int status;
/**
* The position of this label in the code, if known.
*/
int position;
/**
* Number of forward references to this label, times two.
*/
private int referenceCount;
/**
* Informations about forward references. Each forward reference is described by two consecutive integers in this
* array: the first one is the position of the first byte of the bytecode instruction that contains the forward
* reference, while the second is the position of the first byte of the forward reference itself. In fact the sign
* of the first integer indicates if this reference uses 2 or 4 bytes, and its absolute value gives the position of
* the bytecode instruction. This array is also used as a bitset to store the subroutines to which a basic block
* belongs. This information is needed in MethodWriter#visitMaxs, after all forward references have been
* resolved. Hence the same array can be used for both purposes without problems.
*/
private int[] srcAndRefPositions;
/**
* The bit mask to extract the type of a forward reference to this label. The extracted type is
* either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link #FORWARD_REFERENCE_TYPE_WIDE}.
*/
static final int FORWARD_REFERENCE_TYPE_MASK = 0xF0000000;
/**
* The bit mask to extract the 'handle' of a forward reference to this label. The extracted handle
* is the bytecode offset where the forward reference value is stored (using either 2 or 4 bytes,
* as indicated by the {@link #FORWARD_REFERENCE_TYPE_MASK}).
*/
static final int FORWARD_REFERENCE_HANDLE_MASK = 0x0FFFFFFF;
/**
* The type of forward references stored with two bytes in the bytecode. This is the case, for
* instance, of a forward reference from an ifnull instruction.
*/
static final int FORWARD_REFERENCE_TYPE_SHORT = 0x10000000;
/**
* The type of forward references stored in four bytes in the bytecode. This is the case, for
* instance, of a forward reference from a lookupswitch instruction.
*/
static final int FORWARD_REFERENCE_TYPE_WIDE = 0x20000000;
// ------------------------------------------------------------------------
/*
* Fields for the control flow and data flow graph analysis algorithms (used to compute the maximum stack size or
* the stack map frames). A control flow graph contains one node per "basic block", and one edge per "jump" from one
* basic block to another. Each node (i.e., each basic block) is represented by the Label object that corresponds to
* the first instruction of this basic block. Each node also stores the list of its successors in the graph, as a
* linked list of Edge objects. The control flow analysis algorithms used to compute the maximum stack size or the
* stack map frames are similar and use two steps. The first step, during the visit of each instruction, builds
* information about the state of the local variables and the operand stack at the end of each basic block, called
* the "output frame", relatively to the frame state at the beginning of the basic block, which is called the
* "input frame", and which is unknown during this step. The second step, in link MethodWriter#visitMaxs,
* is a fix point algorithm that computes information about the input frame of each basic block, from the input
* state of the first basic block (known from the method signature), and by the using the previously computed
* relative output frames. The algorithm used to compute the maximum stack size only computes the relative output
* and absolute input stack heights, while the algorithm used to compute stack map frames computes relative output
* frames and absolute input frames.
*/
/**
* Start of the output stack relatively to the input stack. The exact semantics of this field depends on the
* algorithm that is used. When only the maximum stack size is computed, this field is the number of elements in the
* input stack. When the stack map frames are completely computed, this field is the offset of the first output
* stack element relatively to the top of the input stack. This offset is always negative or null. A null offset
* means that the output stack must be appended to the input stack. A -n offset means that the first n output stack
* elements must replace the top n input stack elements, and that the other elements must be appended to the input
* stack.
*/
int inputStackTop;
/**
* Maximum height reached by the output stack, relatively to the top of the input stack. This maximum is always
* positive or null.
*/
int outputStackMax;
/**
* The successor of this label, in the order they are visited. This linked list does not include labels used for
* debug info only. If ClassWriter#COMPUTE_FRAMES option is used then, in addition, it does not contain
* successive labels that denote the same bytecode position (in this case only the first label appears in this
* list).
*/
Label successor;
/**
* The next basic block in the basic block stack. This stack is used in the main loop of the fix point algorithm
* used in the second step of the control flow analysis algorithms. It is also used in {@link #visitSubroutine} to
* avoid using a recursive method.
*
* @see MethodWriter#visitMaxs
*/
Label next;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Constructs a new label.
*/
public Label(){
}
// ------------------------------------------------------------------------
// Methods to compute offsets and to manage forward references
// ------------------------------------------------------------------------
/**
* Puts a reference to this label in the bytecode of a method. If the position of the label is known, the offset is
* computed and written directly. Otherwise, a null offset is written and a new forward reference is declared for
* this label.
*
* @param owner the code writer that calls this method.
* @param out the bytecode of the method.
* @param source the position of first byte of the bytecode instruction that contains this label.
* @param wideOffset true if the reference must be stored in 4 bytes, or false if it must be
* stored with 2 bytes.
* @throws IllegalArgumentException if this label has not been created by the given code writer.
*/
void put(final MethodWriter owner, final ByteVector out, final int source, boolean wideOffset) {
if ((status & 2 /* RESOLVED */) == 0) {
if (wideOffset) {
addReference(source, out.length, FORWARD_REFERENCE_TYPE_WIDE);
out.putInt(-1);
} else {
addReference(source, out.length, FORWARD_REFERENCE_TYPE_SHORT);
out.putShort(-1);
}
} else {
if (wideOffset) {
out.putInt(position - source);
} else {
out.putShort(position - source);
}
}
}
/**
* Adds a forward reference to this label. This method must be called only for a true forward reference, i.e. only
* if this label is not resolved yet. For backward references, the offset of the reference can be, and must be,
* computed and stored directly.
*
* @param sourcePosition the position of the referencing instruction. This position will be used to compute the
* offset of this forward reference.
* @param referencePosition the position where the offset for this forward reference must be stored.
*/
private void addReference(final int sourcePosition, final int referencePosition, final int referenceType) {
if (srcAndRefPositions == null) {
srcAndRefPositions = new int[6];
}
if (referenceCount >= srcAndRefPositions.length) {
int[] a = new int[srcAndRefPositions.length + 6];
System.arraycopy(srcAndRefPositions, 0, a, 0, srcAndRefPositions.length);
srcAndRefPositions = a;
}
srcAndRefPositions[referenceCount++] = sourcePosition;
srcAndRefPositions[referenceCount++] = referencePosition | referenceType;
}
/**
* Resolves all forward references to this label. This method must be called when this label is added to the
* bytecode of the method, i.e. when its position becomes known. This method fills in the blanks that where left in
* the bytecode by each forward reference previously added to this label.
*
* @param owner the code writer that calls this method.
* @param position the position of this label in the bytecode.
* @param data the bytecode of the method.
* @return true if a blank that was left for this label was to small to store the offset. In such a case
* the corresponding jump instruction is replaced with a pseudo instruction (using unused opcodes) using an unsigned
* two bytes offset. These pseudo instructions will need to be replaced with true instructions with wider offsets (4
* bytes instead of 2). This is done in {@link MethodWriter#resizeInstructions}.
* @throws IllegalArgumentException if this label has already been resolved, or if it has not been created by the
* given code writer.
*/
void resolve(final MethodWriter owner, final int position, final byte[] data) {
this.status |= 2 /* RESOLVED */ ;
this.position = position;
int i = 0;
while (i < referenceCount) {
int source = srcAndRefPositions[i++];
int reference = srcAndRefPositions[i++];
int handle = reference & FORWARD_REFERENCE_HANDLE_MASK;
int offset = position - source;
if ((reference & FORWARD_REFERENCE_TYPE_MASK) == FORWARD_REFERENCE_TYPE_SHORT) {
data[handle++] = (byte) (offset >>> 8);
data[handle] = (byte) offset;
} else {
data[handle++] = (byte) (offset >>> 24);
data[handle++] = (byte) (offset >>> 16);
data[handle++] = (byte) (offset >>> 8);
data[handle] = (byte) offset;
}
}
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/MethodCollector.java
================================================
package com.alibaba.fastjson.asm;
/**
* Created by wenshao on 05/08/2017.
*/
public class MethodCollector {
private final int paramCount;
private final int ignoreCount;
private int currentParameter;
private final StringBuilder result;
protected boolean debugInfoPresent;
protected MethodCollector(int ignoreCount, int paramCount) {
this.ignoreCount = ignoreCount;
this.paramCount = paramCount;
this.result = new StringBuilder();
this.currentParameter = 0;
// if there are 0 parameters, there is no need for debug info
this.debugInfoPresent = paramCount == 0;
}
protected void visitLocalVariable(String name, int index) {
if (index >= ignoreCount && index < ignoreCount + paramCount) {
if (!name.equals("arg" + currentParameter)) {
debugInfoPresent = true;
}
result.append(',');
result.append(name);
currentParameter++;
}
}
protected String getResult() {
return result.length() != 0 ? result.substring(1) : "";
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/MethodVisitor.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2007 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.alibaba.fastjson.asm;
/**
*
* @author Eric Bruneton
*/
public interface MethodVisitor {
// -------------------------------------------------------------------------
// Annotations and non standard attributes
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// Normal instructions
// -------------------------------------------------------------------------
/**
* Visits a zero operand instruction.
*
* @param opcode the opcode of the instruction to be visited. This opcode is either NOP, ACONST_NULL, ICONST_M1,
* ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2,
* DCONST_0, DCONST_1, IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE,
* DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, IADD,
* LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM,
* INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D,
* L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN,
* FRETURN, DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER, or MONITOREXIT.
*/
void visitInsn(int opcode);
void visitIntInsn(int opcode, int operand);
/**
* Visits a local variable instruction. A local variable instruction is an instruction that loads or stores the
* value of a local variable.
*
* @param opcode the opcode of the local variable instruction to be visited. This opcode is either ILOAD, LLOAD,
* FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET.
* @param var the operand of the instruction to be visited. This operand is the index of a local variable.
*/
void visitVarInsn(int opcode, int var);
/**
* Visits a type instruction. A type instruction is an instruction that takes the internal name of a class as
* parameter.
*
* @param opcode the opcode of the type instruction to be visited. This opcode is either NEW, ANEWARRAY, CHECKCAST
* or INSTANCEOF.
* @param type the operand of the instruction to be visited. This operand must be the internal name of an object or
* array class (see {@link Type#getInternalName() getInternalName}).
*/
void visitTypeInsn(int opcode, String type);
/**
* Visits a field instruction. A field instruction is an instruction that loads or stores the value of a field of an
* object.
*
* @param opcode the opcode of the type instruction to be visited. This opcode is either GETSTATIC, PUTSTATIC,
* GETFIELD or PUTFIELD.
* @param owner the internal name of the field's owner class (see {@link Type#getInternalName() getInternalName}).
* @param name the field's name.
* @param desc the field's descriptor (see {@link Type Type}).
*/
void visitFieldInsn(int opcode, String owner, String name, String desc);
void visitMethodInsn(int opcode, String owner, String name, String desc);
/**
* Visits a jump instruction. A jump instruction is an instruction that may jump to another instruction.
*
* @param opcode the opcode of the type instruction to be visited. This opcode is either IFEQ, IFNE, IFLT, IFGE,
* IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, GOTO, JSR,
* IFNULL or IFNONNULL.
* @param label the operand of the instruction to be visited. This operand is a label that designates the
* instruction to which the jump instruction may jump.
*/
void visitJumpInsn(int opcode, Label label);
/**
* Visits a label. A label designates the instruction that will be visited just after it.
*
* @param label a {@link Label Label} object.
*/
void visitLabel(Label label);
// -------------------------------------------------------------------------
// Special instructions
// -------------------------------------------------------------------------
/**
* Visits a LDC instruction.
*
* @param cst the constant to be loaded on the stack. This parameter must be a non null {@link Integer}, a
* {@link Float}, a {@link Long}, a {@link Double} a {@link String} (or a {@link Type} for .class
* constants, for classes whose version is 49.0 or more).
*/
void visitLdcInsn(Object cst);
/**
* Visits an IINC instruction.
*
* @param var index of the local variable to be incremented.
* @param increment amount to increment the local variable by.
*/
void visitIincInsn(int var, int increment);
// -------------------------------------------------------------------------
// Exceptions table entries, debug information, max stack and max locals
// -------------------------------------------------------------------------
/**
* Visits the maximum stack size and the maximum number of local variables of the method.
*
* @param maxStack maximum stack size of the method.
* @param maxLocals maximum number of local variables for the method.
*/
void visitMaxs(int maxStack, int maxLocals);
/**
* Visits the end of the method. This method, which is the last one to be called, is used to inform the visitor that
* all the annotations and attributes of the method have been visited.
*/
void visitEnd();
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/MethodWriter.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2007 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.alibaba.fastjson.asm;
/**
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
public class MethodWriter implements MethodVisitor {
/**
* Next method writer (see {@link ClassWriter#firstMethod firstMethod}).
*/
MethodWriter next;
/**
* The class writer to which this method must be added.
*/
final ClassWriter cw;
/**
* Access flags of this method.
*/
private int access;
/**
* The index of the constant pool item that contains the name of this method.
*/
private final int name;
/**
* The index of the constant pool item that contains the descriptor of this method.
*/
private final int desc;
/**
* Number of exceptions that can be thrown by this method.
*/
int exceptionCount;
/**
* The exceptions that can be thrown by this method. More precisely, this array contains the indexes of the constant
* pool items that contain the internal names of these exception classes.
*/
int[] exceptions;
/**
* The bytecode of this method.
*/
private ByteVector code = new ByteVector();
/**
* Maximum stack size of this method.
*/
private int maxStack;
/**
* Maximum number of local variables for this method.
*/
private int maxLocals;
// ------------------------------------------------------------------------
/*
* Fields for the control flow graph analysis algorithm (used to compute the maximum stack size). A control flow
* graph contains one node per "basic block", and one edge per "jump" from one basic block to another. Each node
* (i.e., each basic block) is represented by the Label object that corresponds to the first instruction of this
* basic block. Each node also stores the list of its successors in the graph, as a linked list of Edge objects.
*/
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
public MethodWriter(final ClassWriter cw, final int access, final String name, final String desc, final String signature, final String[] exceptions){
if (cw.firstMethod == null) {
cw.firstMethod = this;
} else {
cw.lastMethod.next = this;
}
cw.lastMethod = this;
this.cw = cw;
this.access = access;
this.name = cw.newUTF8(name);
this.desc = cw.newUTF8(desc);
if (exceptions != null && exceptions.length > 0) {
exceptionCount = exceptions.length;
this.exceptions = new int[exceptionCount];
for (int i = 0; i < exceptionCount; ++i) {
this.exceptions[i] = cw.newClassItem(exceptions[i]).index;
}
}
}
// ------------------------------------------------------------------------
// Implementation of the MethodVisitor interface
// ------------------------------------------------------------------------
public void visitInsn(final int opcode) {
// adds the instruction to the bytecode of the method
code.putByte(opcode);
// update currentBlock
// Label currentBlock = this.currentBlock;
}
public void visitIntInsn(final int opcode, final int operand) {
// Label currentBlock = this.currentBlock;
// adds the instruction to the bytecode of the method
// if (opcode == Opcodes.SIPUSH) {
// code.put12(opcode, operand);
// } else { // BIPUSH or NEWARRAY
code.put11(opcode, operand);
// }
}
public void visitVarInsn(final int opcode, final int var) {
// Label currentBlock = this.currentBlock;
// adds the instruction to the bytecode of the method
if (var < 4 && opcode != Opcodes.RET) {
int opt;
if (opcode < Opcodes.ISTORE) {
/* ILOAD_0 */
opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;
} else {
/* ISTORE_0 */
opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;
}
code.putByte(opt);
} else if (var >= 256) {
code.putByte(196 /* WIDE */).put12(opcode, var);
} else {
code.put11(opcode, var);
}
}
public void visitTypeInsn(final int opcode, final String type) {
Item i = cw.newClassItem(type);
// Label currentBlock = this.currentBlock;
// adds the instruction to the bytecode of the method
code.put12(opcode, i.index);
}
public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
Item i = cw.newFieldItem(owner, name, desc);
// Label currentBlock = this.currentBlock;
// adds the instruction to the bytecode of the method
code.put12(opcode, i.index);
}
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
boolean itf = opcode == Opcodes.INVOKEINTERFACE;
Item i = cw.newMethodItem(owner, name, desc, itf);
int argSize = i.intVal;
// Label currentBlock = this.currentBlock;
// adds the instruction to the bytecode of the method
if (itf) {
if (argSize == 0) {
argSize = Type.getArgumentsAndReturnSizes(desc);
i.intVal = argSize;
}
code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
} else {
code.put12(opcode, i.index);
}
}
public void visitJumpInsn(final int opcode, final Label label) {
// Label currentBlock = this.currentBlock;
// adds the instruction to the bytecode of the method
if ((label.status & 2 /* Label.RESOLVED */ ) != 0 && label.position - code.length < Short.MIN_VALUE) {
throw new UnsupportedOperationException();
} else {
/*
* case of a backward jump with an offset >= -32768, or of a forward jump with, of course, an unknown
* offset. In these cases we store the offset in 2 bytes (which will be increased in resizeInstructions, if
* needed).
*/
code.putByte(opcode);
// Currently, GOTO_W is the only supported wide reference
label.put(this, code, code.length - 1, opcode == Opcodes.GOTO_W);
}
}
public void visitLabel(final Label label) {
// resolves previous forward references to label, if any
label.resolve(this, code.length, code.data);
}
public void visitLdcInsn(final Object cst) {
Item i = cw.newConstItem(cst);
// Label currentBlock = this.currentBlock;
// adds the instruction to the bytecode of the method
int index = i.index;
if (i.type == 5 /* ClassWriter.LONG */ || i.type == 6 /* ClassWriter.DOUBLE */) {
code.put12(20 /* LDC2_W */, index);
} else if (index >= 256) {
code.put12(19 /* LDC_W */, index);
} else {
code.put11(18 /*Opcodes.LDC*/, index);
}
}
public void visitIincInsn(final int var, final int increment) {
// adds the instruction to the bytecode of the method
// if ((var > 255) || (increment > 127) || (increment < -128)) {
// code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var).putShort(increment);
// } else {
code.putByte(132 /* Opcodes.IINC*/ ).put11(var, increment);
// }
}
public void visitMaxs(final int maxStack, final int maxLocals) {
this.maxStack = maxStack;
this.maxLocals = maxLocals;
}
public void visitEnd() {
}
// ------------------------------------------------------------------------
// Utility methods: control flow analysis algorithm
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Utility methods: stack map frames
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Utility methods: dump bytecode array
// ------------------------------------------------------------------------
/**
* Returns the size of the bytecode of this method.
*
* @return the size of the bytecode of this method.
*/
final int getSize() {
int size = 8;
if (code.length > 0) {
cw.newUTF8("Code");
size += 18 + code.length + 8 * 0;
}
if (exceptionCount > 0) {
cw.newUTF8("Exceptions");
size += 8 + 2 * exceptionCount;
}
return size;
}
/**
* Puts the bytecode of this method in the given byte vector.
*
* @param out the byte vector into which the bytecode of this method must be copied.
*/
final void put(final ByteVector out) {
final int mask = 393216; //Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / (ClassWriter.ACC_SYNTHETIC_ATTRIBUTE / Opcodes.ACC_SYNTHETIC));
out.putShort(access & ~mask).putShort(name).putShort(desc);
int attributeCount = 0;
if (code.length > 0) {
++attributeCount;
}
if (exceptionCount > 0) {
++attributeCount;
}
out.putShort(attributeCount);
if (code.length > 0) {
int size = 12 + code.length + 8 * 0; // handlerCount
out.putShort(cw.newUTF8("Code")).putInt(size);
out.putShort(maxStack).putShort(maxLocals);
out.putInt(code.length).putByteArray(code.data, 0, code.length);
out.putShort(0); // handlerCount
attributeCount = 0;
out.putShort(attributeCount);
}
if (exceptionCount > 0) {
out.putShort(cw.newUTF8("Exceptions")).putInt(2 * exceptionCount + 2);
out.putShort(exceptionCount);
for (int i = 0; i < exceptionCount; ++i) {
out.putShort(exceptions[i]);
}
}
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/Opcodes.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2007 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.alibaba.fastjson.asm;
/**
* Defines the JVM opcodes, access flags and array type codes. This interface does not define all the JVM opcodes
* because some opcodes are automatically handled. For example, the xLOAD and xSTORE opcodes are automatically replaced
* by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n opcodes are therefore not defined in this
* interface. Likewise for LDC, automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and JSR_W.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
public interface Opcodes {
int T_INT = 10;
// versions
// int V1_1 = 3 << 16 | 45;
// int V1_2 = 0 << 16 | 46;
// int V1_3 = 0 << 16 | 47;
// int V1_4 = 0 << 16 | 48;
int V1_5 = 0 << 16 | 49;
// int V1_6 = 0 << 16 | 50;
// int V1_7 = 0 << 16 | 51;
// access flags
int ACC_PUBLIC = 0x0001; // class, field, method
int ACC_SUPER = 0x0020; // class
// opcodes // visit method (- = idem)
int ACONST_NULL = 1; // -
int ICONST_0 = 3; // -
int ICONST_1 = 4; // -
int LCONST_0 = 9; // -
int LCONST_1 = 10; // -
int FCONST_0 = 11; // -
int DCONST_0 = 14; // -
int BIPUSH = 16; // visitIntInsn
// int SIPUSH = 17; // -
// int LDC = 18; // visitLdcInsn
// int LDC_W = 19; // -
// int LDC2_W = 20; // -
int ILOAD = 21; // visitVarInsn
int LLOAD = 22; // -
int FLOAD = 23; // -
int DLOAD = 24; // -
int ALOAD = 25; // -
int ISTORE = 54; // visitVarInsn
int LSTORE = 55; // -
int FSTORE = 56; // -
int DSTORE = 57; // -
int ASTORE = 58; // -
int IASTORE = 79; // visitInsn
int POP = 87; // -
// int POP2 = 88; // -
int DUP = 89; // -
int IADD = 96; // -
// int ISUB = 100; // -
int IAND = 126; // -
// int LAND = 127; // -
int IOR = 128; // -
// int LOR = 129; // -
// int IXOR = 130; // -
// int LXOR = 131; // -
// int IINC = 132; // visitIincInsn
int LCMP = 148; // -
int FCMPL = 149; // -
int DCMPL = 151; // -
int IFEQ = 153; // visitJumpInsn
int IFNE = 154; // -
int IFLE = 158; // -
int IF_ICMPEQ = 159; // -
int IF_ICMPNE = 160; // -
int IF_ICMPLT = 161; // -
int IF_ICMPGE = 162; // -
int IF_ICMPGT = 163; // -
int IF_ACMPEQ = 165; // -
int IF_ACMPNE = 166; // -
int GOTO = 167; // -
int RET = 169; // visitVarInsn
int ARETURN = 176; // -
int RETURN = 177; // -
int GETSTATIC = 178; // visitFieldInsn
int GETFIELD = 180; // -
int PUTFIELD = 181; // -
int INVOKEVIRTUAL = 182; // visitMethodInsn
int INVOKESPECIAL = 183; // -
int INVOKESTATIC = 184; // -
int INVOKEINTERFACE = 185; // -
// int INVOKEDYNAMIC = 186; // -
int NEW = 187; // visitTypeInsn
int NEWARRAY = 188; // visitIntInsn
// int ANEWARRAY = 189; // visitTypeInsn
// int ARRAYLENGTH = 190; // visitInsn
// int ATHROW = 191; // -
int CHECKCAST = 192; // visitTypeInsn
int INSTANCEOF = 193;
int IFNULL = 198; // visitJumpInsn
int IFNONNULL = 199; // -
int GOTO_W = 200; // visitJumpInsn
// int JSR_W = 201; // -
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/Type.java
================================================
/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2007 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.alibaba.fastjson.asm;
/**
* A Java type. This class can be used to make it easier to manipulate type and method descriptors.
*
* @author Eric Bruneton
* @author Chris Nokleberg
*/
public class Type {
/**
* The void type.
*/
public static final Type VOID_TYPE = new Type(0, null, ('V' << 24) | (5 << 16) | (0 << 8) | 0, 1);
/**
* The boolean type.
*/
public static final Type BOOLEAN_TYPE = new Type(1, null, ('Z' << 24) | (0 << 16) | (5 << 8) | 1, 1);
/**
* The char type.
*/
public static final Type CHAR_TYPE = new Type(2, null, ('C' << 24) | (0 << 16) | (6 << 8) | 1, 1);
/**
* The byte type.
*/
public static final Type BYTE_TYPE = new Type(3, null, ('B' << 24) | (0 << 16) | (5 << 8) | 1, 1);
/**
* The short type.
*/
public static final Type SHORT_TYPE = new Type(4, null, ('S' << 24) | (0 << 16) | (7 << 8) | 1, 1);
/**
* The int type.
*/
public static final Type INT_TYPE = new Type(5, null, ('I' << 24) | (0 << 16) | (0 << 8) | 1, 1);
/**
* The float type.
*/
public static final Type FLOAT_TYPE = new Type(6, null, ('F' << 24) | (2 << 16) | (2 << 8) | 1, 1);
/**
* The long type.
*/
public static final Type LONG_TYPE = new Type(7, null, ('J' << 24) | (1 << 16) | (1 << 8) | 2, 1);
/**
* The double type.
*/
public static final Type DOUBLE_TYPE = new Type(8, null, ('D' << 24) | (3 << 16) | (3 << 8) | 2, 1);
// ------------------------------------------------------------------------
// Fields
// ------------------------------------------------------------------------
/**
* The sort of this Java type.
*/
protected final int sort;
/**
* A buffer containing the internal name of this Java type. This field is only used for reference types.
*/
private final char[] buf;
/**
* The offset of the internal name of this Java type in {@link #buf buf} or, for primitive types, the size,
* descriptor and getOpcode offsets for this type (byte 0 contains the size, byte 1 the descriptor, byte 2 the
* offset for IALOAD or IASTORE, byte 3 the offset for all other instructions).
*/
private final int off;
/**
* The length of the internal name of this Java type.
*/
private final int len;
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
private Type(final int sort, final char[] buf, final int off, final int len){
this.sort = sort;
this.buf = buf;
this.off = off;
this.len = len;
}
/**
* Returns the Java type corresponding to the given type descriptor.
*
* @param typeDescriptor a type descriptor.
* @return the Java type corresponding to the given type descriptor.
*/
public static Type getType(final String typeDescriptor) {
return getType(typeDescriptor.toCharArray(), 0);
}
public static int getArgumentsAndReturnSizes(final String desc) {
int n = 1;
int c = 1;
while (true) {
char car = desc.charAt(c++);
if (car == ')') {
car = desc.charAt(c);
return n << 2 | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1));
} else if (car == 'L') {
while (desc.charAt(c++) != ';') {
}
n += 1;
// } else if (car == '[') {
// while ((car = desc.charAt(c)) == '[') {
// ++c;
// }
// if (car == 'D' || car == 'J') {
// n -= 1;
// }
} else if (car == 'D' || car == 'J') {
n += 2;
} else {
n += 1;
}
}
}
/**
* Returns the Java type corresponding to the given type descriptor.
*
* @param buf a buffer containing a type descriptor.
* @param off the offset of this descriptor in the previous buffer.
* @return the Java type corresponding to the given type descriptor.
*/
private static Type getType(final char[] buf, final int off) {
int len;
switch (buf[off]) {
case 'V':
return VOID_TYPE;
case 'Z':
return BOOLEAN_TYPE;
case 'C':
return CHAR_TYPE;
case 'B':
return BYTE_TYPE;
case 'S':
return SHORT_TYPE;
case 'I':
return INT_TYPE;
case 'F':
return FLOAT_TYPE;
case 'J':
return LONG_TYPE;
case 'D':
return DOUBLE_TYPE;
case '[':
len = 1;
while (buf[off + len] == '[') {
++len;
}
if (buf[off + len] == 'L') {
++len;
while (buf[off + len] != ';') {
++len;
}
}
return new Type(9 /*ARRAY*/, buf, off, len + 1);
// case 'L':
default:
len = 1;
while (buf[off + len] != ';') {
++len;
}
return new Type(10/*OBJECT*/, buf, off + 1, len - 1);
}
}
public String getInternalName() {
return new String(buf, off, len);
}
// ------------------------------------------------------------------------
// Conversion to type descriptors
// ------------------------------------------------------------------------
/**
* Returns the descriptor corresponding to this Java type.
*
* @return the descriptor corresponding to this Java type.
*/
String getDescriptor() {
return new String(this.buf, off, len);
}
private int getDimensions() {
int i = 1;
while (buf[off + i] == '[') {
++i;
}
return i;
}
static Type[] getArgumentTypes(final String methodDescriptor) {
char[] buf = methodDescriptor.toCharArray();
int off = 1;
int size = 0;
for (;;) {
char car = buf[off++];
if (car == ')') {
break;
} else if (car == 'L') {
while (buf[off++] != ';') {
}
++size;
} else if (car != '[') {
++size;
}
}
Type[] args = new Type[size];
off = 1;
size = 0;
while (buf[off] != ')') {
args[size] = getType(buf, off);
off += args[size].len + (args[size].sort == 10 /*OBJECT*/ ? 2 : 0);
size += 1;
}
return args;
}
protected String getClassName() {
switch (sort) {
case 0: //VOID:
return "void";
case 1: //BOOLEAN:
return "boolean";
case 2: //CHAR:
return "char";
case 3: //BYTE:
return "byte";
case 4: //SHORT:
return "short";
case 5: //INT:
return "int";
case 6: //FLOAT:
return "float";
case 7: //LONG:
return "long";
case 8: //DOUBLE:
return "double";
case 9: //ARRAY:
Type elementType = getType(buf, off + getDimensions());
StringBuilder b = new StringBuilder(elementType.getClassName());
for (int i = getDimensions(); i > 0; --i) {
b.append("[]");
}
return b.toString();
// case OBJECT:
default:
return new String(buf, off, len).replace('/', '.');
}
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/asm/TypeCollector.java
================================================
package com.alibaba.fastjson.asm;
import com.alibaba.fastjson.util.ASMUtils;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
public class TypeCollector {
private static String JSONType = ASMUtils.desc(com.alibaba.fastjson.annotation.JSONType.class);
private static final Map primitives = new HashMap() {
{
put("int","I");
put("boolean","Z");
put("byte", "B");
put("char","C");
put("short","S");
put("float","F");
put("long","J");
put("double","D");
}
};
private final String methodName;
private final Class>[] parameterTypes;
protected MethodCollector collector;
protected boolean jsonType;
public TypeCollector(String methodName, Class>[] parameterTypes) {
this.methodName = methodName;
this.parameterTypes = parameterTypes;
this.collector = null;
}
protected MethodCollector visitMethod(int access, String name, String desc) {
if (collector != null) {
return null;
}
if (!name.equals(methodName)) {
return null;
}
Type[] argTypes = Type.getArgumentTypes(desc);
int longOrDoubleQuantity = 0;
for (Type t : argTypes) {
String className = t.getClassName();
if (className.equals("long") || className.equals("double")) {
longOrDoubleQuantity++;
}
}
if (argTypes.length != this.parameterTypes.length) {
return null;
}
for (int i = 0; i < argTypes.length; i++) {
if (!correctTypeName(argTypes[i], this.parameterTypes[i].getName())) {
return null;
}
}
return collector = new MethodCollector(
Modifier.isStatic(access) ? 0 : 1,
argTypes.length + longOrDoubleQuantity);
}
public void visitAnnotation(String desc) {
if (JSONType.equals(desc)) {
jsonType = true;
}
}
private boolean correctTypeName(Type type, String paramTypeName) {
String s = type.getClassName();
// array notation needs cleanup.
StringBuilder braces = new StringBuilder();
while (s.endsWith("[]")) {
braces.append('[');
s = s.substring(0, s.length() - 2);
}
if (braces.length() != 0) {
if (primitives.containsKey(s)) {
s = braces.append(primitives.get(s)).toString();
} else {
s = braces.append('L').append(s).append(';').toString();
}
}
return s.equals(paramTypeName);
}
public String[] getParameterNamesForMethod() {
if (collector == null || !collector.debugInfoPresent) {
return new String[0];
}
return collector.getResult().split(",");
}
public boolean matched() {
return collector != null;
}
public boolean hasJsonType() {
return jsonType;
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/DefaultExtJSONParser.java
================================================
/*
* Copyright 1999-2017 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.fastjson.parser;
/**
* @author wenshao[szujobs@hotmail.com]
*/
@Deprecated
public class DefaultExtJSONParser extends DefaultJSONParser {
public DefaultExtJSONParser(String input){
this(input, ParserConfig.getGlobalInstance());
}
public DefaultExtJSONParser(String input, ParserConfig mapping){
super(input, mapping);
}
public DefaultExtJSONParser(String input, ParserConfig mapping, int features){
super(input, mapping, features);
}
public DefaultExtJSONParser(char[] input, int length, ParserConfig mapping, int features){
super(input, length, mapping, features);
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/DefaultJSONParser.java
================================================
/*
* Copyright 1999-2019 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.fastjson.parser;
import com.alibaba.fastjson.*;
import com.alibaba.fastjson.parser.deserializer.*;
import com.alibaba.fastjson.serializer.*;
import com.alibaba.fastjson.util.TypeUtils;
import java.io.Closeable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import static com.alibaba.fastjson.parser.JSONLexer.EOI;
import static com.alibaba.fastjson.parser.JSONToken.*;
/**
* @author wenshao[szujobs@hotmail.com]
*/
public class DefaultJSONParser implements Closeable {
public final Object input;
public final SymbolTable symbolTable;
protected ParserConfig config;
private final static Set> primitiveClasses = new HashSet>();
private String dateFormatPattern = JSON.DEFFAULT_DATE_FORMAT;
private DateFormat dateFormat;
public final JSONLexer lexer;
protected ParseContext context;
private ParseContext[] contextArray;
private int contextArrayIndex = 0;
private List resolveTaskList;
public final static int NONE = 0;
public final static int NeedToResolve = 1;
public final static int TypeNameRedirect = 2;
public int resolveStatus = NONE;
private List extraTypeProviders = null;
private List extraProcessors = null;
protected FieldTypeResolver fieldTypeResolver = null;
private int objectKeyLevel = 0;
private boolean autoTypeEnable;
private String[] autoTypeAccept = null;
protected transient BeanContext lastBeanContext;
static {
Class>[] classes = new Class[] {
boolean.class,
byte.class,
short.class,
int.class,
long.class,
float.class,
double.class,
Boolean.class,
Byte.class,
Short.class,
Integer.class,
Long.class,
Float.class,
Double.class,
BigInteger.class,
BigDecimal.class,
String.class
};
primitiveClasses.addAll(Arrays.asList(classes));
}
public String getDateFomartPattern() {
return dateFormatPattern;
}
public DateFormat getDateFormat() {
if (dateFormat == null) {
dateFormat = new SimpleDateFormat(dateFormatPattern, lexer.getLocale());
dateFormat.setTimeZone(lexer.getTimeZone());
}
return dateFormat;
}
public void setDateFormat(String dateFormat) {
this.dateFormatPattern = dateFormat;
this.dateFormat = null;
}
/**
* @deprecated
* @see setDateFormat
*/
public void setDateFomrat(DateFormat dateFormat) {
this.setDateFormat(dateFormat);
}
public void setDateFormat(DateFormat dateFormat) {
this.dateFormat = dateFormat;
}
public DefaultJSONParser(String input){
this(input, ParserConfig.getGlobalInstance(), JSON.DEFAULT_PARSER_FEATURE);
}
public DefaultJSONParser(final String input, final ParserConfig config){
this(input, new JSONScanner(input, JSON.DEFAULT_PARSER_FEATURE), config);
}
public DefaultJSONParser(final String input, final ParserConfig config, int features){
this(input, new JSONScanner(input, features), config);
}
public DefaultJSONParser(final char[] input, int length, final ParserConfig config, int features){
this(input, new JSONScanner(input, length, features), config);
}
public DefaultJSONParser(final JSONLexer lexer){
this(lexer, ParserConfig.getGlobalInstance());
}
public DefaultJSONParser(final JSONLexer lexer, final ParserConfig config){
this(null, lexer, config);
}
public DefaultJSONParser(final Object input, final JSONLexer lexer, final ParserConfig config){
this.lexer = lexer;
this.input = input;
this.config = config;
this.symbolTable = config.symbolTable;
int ch = lexer.getCurrent();
if (ch == '{') {
lexer.next();
((JSONLexerBase) lexer).token = JSONToken.LBRACE;
} else if (ch == '[') {
lexer.next();
((JSONLexerBase) lexer).token = JSONToken.LBRACKET;
} else {
lexer.nextToken(); // prime the pump
}
}
public SymbolTable getSymbolTable() {
return symbolTable;
}
public String getInput() {
if (input instanceof char[]) {
return new String((char[]) input);
}
return input.toString();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public final Object parseObject(final Map object, Object fieldName) {
final JSONLexer lexer = this.lexer;
if (lexer.token() == JSONToken.NULL) {
lexer.nextToken();
return null;
}
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
return object;
}
if (lexer.token() == JSONToken.LITERAL_STRING && lexer.stringVal().length() == 0) {
lexer.nextToken();
return object;
}
if (lexer.token() != JSONToken.LBRACE && lexer.token() != JSONToken.COMMA) {
throw new JSONException("syntax error, expect {, actual " + lexer.tokenName() + ", " + lexer.info());
}
ParseContext context = this.context;
try {
boolean isJsonObjectMap = object instanceof JSONObject;
Map map = isJsonObjectMap ? ((JSONObject) object).getInnerMap() : object;
boolean setContextFlag = false;
for (;;) {
lexer.skipWhitespace();
char ch = lexer.getCurrent();
if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
while (ch == ',') {
lexer.next();
lexer.skipWhitespace();
ch = lexer.getCurrent();
}
}
boolean isObjectKey = false;
Object key;
if (ch == '"') {
key = lexer.scanSymbol(symbolTable, '"');
lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("expect ':' at " + lexer.pos() + ", name " + key);
}
} else if (ch == '}') {
lexer.next();
lexer.resetStringPosition();
lexer.nextToken();
if (!setContextFlag) {
if (this.context != null && fieldName == this.context.fieldName && object == this.context.object) {
context = this.context;
} else {
ParseContext contextR = setContext(object, fieldName);
if (context == null) {
context = contextR;
}
setContextFlag = true;
}
}
return object;
} else if (ch == '\'') {
if (!lexer.isEnabled(Feature.AllowSingleQuotes)) {
throw new JSONException("syntax error");
}
key = lexer.scanSymbol(symbolTable, '\'');
lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("expect ':' at " + lexer.pos());
}
} else if (ch == EOI) {
throw new JSONException("syntax error");
} else if (ch == ',') {
throw new JSONException("syntax error");
} else if ((ch >= '0' && ch <= '9') || ch == '-') {
lexer.resetStringPosition();
lexer.scanNumber();
try {
if (lexer.token() == JSONToken.LITERAL_INT) {
key = lexer.integerValue();
} else {
key = lexer.decimalValue(true);
}
if (lexer.isEnabled(Feature.NonStringKeyAsString) || isJsonObjectMap) {
key = key.toString();
}
} catch (NumberFormatException e) {
throw new JSONException("parse number key error" + lexer.info());
}
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("parse number key error" + lexer.info());
}
} else if (ch == '{' || ch == '[') {
if (objectKeyLevel++ > 512) {
throw new JSONException("object key level > 512");
}
lexer.nextToken();
key = parse();
isObjectKey = true;
} else {
if (!lexer.isEnabled(Feature.AllowUnQuotedFieldNames)) {
throw new JSONException("syntax error");
}
key = lexer.scanSymbolUnQuoted(symbolTable);
lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("expect ':' at " + lexer.pos() + ", actual " + ch);
}
}
if (!isObjectKey) {
lexer.next();
lexer.skipWhitespace();
}
ch = lexer.getCurrent();
lexer.resetStringPosition();
if (key == JSON.DEFAULT_TYPE_KEY
&& !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
String typeName = lexer.scanSymbol(symbolTable, '"');
if (lexer.isEnabled(Feature.IgnoreAutoType)) {
continue;
}
Class> clazz = null;
if (object != null
&& object.getClass().getName().equals(typeName)) {
clazz = object.getClass();
} else if ("java.util.HashMap".equals(typeName)) {
clazz = java.util.HashMap.class;
} else if ("java.util.LinkedHashMap".equals(typeName)) {
clazz = java.util.LinkedHashMap.class;
} else {
boolean allDigits = true;
for (int i = 0; i < typeName.length(); ++i) {
char c = typeName.charAt(i);
if (c < '0' || c > '9') {
allDigits = false;
break;
}
}
if (!allDigits) {
clazz = config.checkAutoType(typeName, null, lexer.getFeatures());
}
}
if (clazz == null) {
map.put(JSON.DEFAULT_TYPE_KEY, typeName);
continue;
}
lexer.nextToken(JSONToken.COMMA);
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken(JSONToken.COMMA);
try {
Object instance = null;
ObjectDeserializer deserializer = this.config.getDeserializer(clazz);
if (deserializer instanceof JavaBeanDeserializer) {
instance = TypeUtils.cast(object, clazz, this.config);
}
if (instance == null) {
if (clazz == Cloneable.class) {
instance = new HashMap();
} else if ("java.util.Collections$EmptyMap".equals(typeName)) {
instance = Collections.emptyMap();
} else if ("java.util.Collections$UnmodifiableMap".equals(typeName)) {
instance = Collections.unmodifiableMap(new HashMap());
} else {
instance = clazz.newInstance();
}
}
return instance;
} catch (Exception e) {
throw new JSONException("create instance error", e);
}
}
this.setResolveStatus(TypeNameRedirect);
if (this.context != null
&& fieldName != null
&& !(fieldName instanceof Integer)
&& !(this.context.fieldName instanceof Integer)) {
this.popContext();
}
if (object.size() > 0) {
Object newObj = TypeUtils.cast(object, clazz, this.config);
this.setResolveStatus(NONE);
this.parseObject(newObj);
return newObj;
}
ObjectDeserializer deserializer = config.getDeserializer(clazz);
Class deserClass = deserializer.getClass();
if (JavaBeanDeserializer.class.isAssignableFrom(deserClass)
&& deserClass != JavaBeanDeserializer.class
&& deserClass != ThrowableDeserializer.class) {
this.setResolveStatus(NONE);
} else if (deserializer instanceof MapDeserializer) {
this.setResolveStatus(NONE);
}
Object obj = deserializer.deserialze(this, clazz, fieldName);
return obj;
}
if (key == "$ref"
&& context != null
&& (object == null || object.size() == 0)
&& !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
lexer.nextToken(JSONToken.LITERAL_STRING);
if (lexer.token() == JSONToken.LITERAL_STRING) {
String ref = lexer.stringVal();
lexer.nextToken(JSONToken.RBRACE);
if (lexer.token() == JSONToken.COMMA) {
map.put(key, ref);
continue;
}
Object refValue = null;
if ("@".equals(ref)) {
if (this.context != null) {
ParseContext thisContext = this.context;
Object thisObj = thisContext.object;
if (thisObj instanceof Object[] || thisObj instanceof Collection>) {
refValue = thisObj;
} else if (thisContext.parent != null) {
refValue = thisContext.parent.object;
}
}
} else if ("..".equals(ref)) {
if (context.object != null) {
refValue = context.object;
} else {
addResolveTask(new ResolveTask(context, ref));
setResolveStatus(DefaultJSONParser.NeedToResolve);
}
} else if ("$".equals(ref)) {
ParseContext rootContext = context;
while (rootContext.parent != null) {
rootContext = rootContext.parent;
}
if (rootContext.object != null) {
refValue = rootContext.object;
} else {
addResolveTask(new ResolveTask(rootContext, ref));
setResolveStatus(DefaultJSONParser.NeedToResolve);
}
} else {
JSONPath jsonpath = JSONPath.compile(ref);
if (jsonpath.isRef()) {
addResolveTask(new ResolveTask(context, ref));
setResolveStatus(DefaultJSONParser.NeedToResolve);
} else {
refValue = new JSONObject()
.fluentPut("$ref", ref);
}
}
if (lexer.token() != JSONToken.RBRACE) {
throw new JSONException("syntax error, " + lexer.info());
}
lexer.nextToken(JSONToken.COMMA);
return refValue;
} else {
throw new JSONException("illegal ref, " + JSONToken.name(lexer.token()));
}
}
if (!setContextFlag) {
if (this.context != null && fieldName == this.context.fieldName && object == this.context.object) {
context = this.context;
} else {
ParseContext contextR = setContext(object, fieldName);
if (context == null) {
context = contextR;
}
setContextFlag = true;
}
}
if (object.getClass() == JSONObject.class) {
if (key == null) {
key = "null";
}
}
Object value;
if (ch == '"') {
lexer.scanString();
String strValue = lexer.stringVal();
value = strValue;
if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {
JSONScanner iso8601Lexer = new JSONScanner(strValue);
if (iso8601Lexer.scanISO8601DateIfMatch()) {
value = iso8601Lexer.getCalendar().getTime();
}
iso8601Lexer.close();
}
map.put(key, value);
} else if (ch >= '0' && ch <= '9' || ch == '-') {
lexer.scanNumber();
if (lexer.token() == JSONToken.LITERAL_INT) {
value = lexer.integerValue();
} else {
value = lexer.decimalValue(lexer.isEnabled(Feature.UseBigDecimal));
}
map.put(key, value);
} else if (ch == '[') { // 减少嵌套,兼容android
lexer.nextToken();
JSONArray list = new JSONArray();
final boolean parentIsArray = fieldName != null && fieldName.getClass() == Integer.class;
// if (!parentIsArray) {
// this.setContext(context);
// }
if (fieldName == null) {
this.setContext(context);
}
this.parseArray(list, key);
if (lexer.isEnabled(Feature.UseObjectArray)) {
value = list.toArray();
} else {
value = list;
}
map.put(key, value);
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
return object;
} else if (lexer.token() == JSONToken.COMMA) {
continue;
} else {
throw new JSONException("syntax error");
}
} else if (ch == '{') { // 减少嵌套,兼容 Android
lexer.nextToken();
final boolean parentIsArray = fieldName != null && fieldName.getClass() == Integer.class;
Map input;
if (lexer.isEnabled(Feature.CustomMapDeserializer)) {
MapDeserializer mapDeserializer = (MapDeserializer) config.getDeserializer(Map.class);
input = (lexer.getFeatures() & Feature.OrderedField.mask) != 0
? mapDeserializer.createMap(Map.class, lexer.getFeatures())
: mapDeserializer.createMap(Map.class);
} else {
input = new JSONObject(lexer.isEnabled(Feature.OrderedField));
}
ParseContext ctxLocal = null;
if (!parentIsArray) {
ctxLocal = setContext(this.context, input, key);
}
Object obj = null;
boolean objParsed = false;
if (fieldTypeResolver != null) {
String resolveFieldName = key != null ? key.toString() : null;
Type fieldType = fieldTypeResolver.resolve(object, resolveFieldName);
if (fieldType != null) {
ObjectDeserializer fieldDeser = config.getDeserializer(fieldType);
obj = fieldDeser.deserialze(this, fieldType, key);
objParsed = true;
}
}
if (!objParsed) {
obj = this.parseObject(input, key);
}
if (ctxLocal != null && input != obj) {
ctxLocal.object = object;
}
if (key != null) {
checkMapResolve(object, key.toString());
}
map.put(key, obj);
if (parentIsArray) {
//setContext(context, obj, key);
setContext(obj, key);
}
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
setContext(context);
return object;
} else if (lexer.token() == JSONToken.COMMA) {
if (parentIsArray) {
this.popContext();
} else {
this.setContext(context);
}
continue;
} else {
throw new JSONException("syntax error, " + lexer.tokenName());
}
} else {
lexer.nextToken();
value = parse();
map.put(key, value);
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
return object;
} else if (lexer.token() == JSONToken.COMMA) {
continue;
} else {
throw new JSONException("syntax error, position at " + lexer.pos() + ", name " + key);
}
}
lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch == ',') {
lexer.next();
continue;
} else if (ch == '}') {
lexer.next();
lexer.resetStringPosition();
lexer.nextToken();
// this.setContext(object, fieldName);
this.setContext(value, key);
return object;
} else {
throw new JSONException("syntax error, position at " + lexer.pos() + ", name " + key);
}
}
} finally {
this.setContext(context);
}
}
public ParserConfig getConfig() {
return config;
}
public void setConfig(ParserConfig config) {
this.config = config;
}
// compatible
@SuppressWarnings("unchecked")
public T parseObject(Class clazz) {
return (T) parseObject(clazz, null);
}
public T parseObject(Type type) {
return parseObject(type, null);
}
@SuppressWarnings("unchecked")
public T parseObject(Type type, Object fieldName) {
int token = lexer.token();
if (token == JSONToken.NULL) {
lexer.nextToken();
return (T) TypeUtils.optionalEmpty(type);
}
if (token == JSONToken.LITERAL_STRING) {
if (type == byte[].class) {
byte[] bytes = lexer.bytesValue();
lexer.nextToken();
return (T) bytes;
}
if (type == char[].class) {
String strVal = lexer.stringVal();
lexer.nextToken();
return (T) strVal.toCharArray();
}
}
ObjectDeserializer deserializer = config.getDeserializer(type);
try {
if (deserializer.getClass() == JavaBeanDeserializer.class) {
if (lexer.token()!= JSONToken.LBRACE && lexer.token()!=JSONToken.LBRACKET) {
throw new JSONException("syntax error,expect start with { or [,but actually start with "+ lexer.tokenName());
}
return (T) ((JavaBeanDeserializer) deserializer).deserialze(this, type, fieldName, 0);
} else {
return (T) deserializer.deserialze(this, type, fieldName);
}
} catch (JSONException e) {
throw e;
} catch (Throwable e) {
throw new JSONException(e.getMessage(), e);
}
}
public List parseArray(Class clazz) {
List array = new ArrayList();
parseArray(clazz, array);
return array;
}
public void parseArray(Class> clazz, @SuppressWarnings("rawtypes") Collection array) {
parseArray((Type) clazz, array);
}
@SuppressWarnings("rawtypes")
public void parseArray(Type type, Collection array) {
parseArray(type, array, null);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public void parseArray(Type type, Collection array, Object fieldName) {
int token = lexer.token();
if (token == JSONToken.SET || token == JSONToken.TREE_SET) {
lexer.nextToken();
token = lexer.token();
}
if (token != JSONToken.LBRACKET) {
throw new JSONException("field " + fieldName + " expect '[', but " + JSONToken.name(token) + ", " + lexer.info());
}
ObjectDeserializer deserializer = null;
if (int.class == type) {
deserializer = IntegerCodec.instance;
lexer.nextToken(JSONToken.LITERAL_INT);
} else if (String.class == type) {
deserializer = StringCodec.instance;
lexer.nextToken(JSONToken.LITERAL_STRING);
} else {
deserializer = config.getDeserializer(type);
lexer.nextToken(deserializer.getFastMatchToken());
}
ParseContext context = this.context;
this.setContext(array, fieldName);
try {
for (int i = 0;; ++i) {
if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
while (lexer.token() == JSONToken.COMMA) {
lexer.nextToken();
continue;
}
}
if (lexer.token() == JSONToken.RBRACKET) {
break;
}
if (int.class == type) {
Object val = IntegerCodec.instance.deserialze(this, null, null);
array.add(val);
} else if (String.class == type) {
String value;
if (lexer.token() == JSONToken.LITERAL_STRING) {
value = lexer.stringVal();
lexer.nextToken(JSONToken.COMMA);
} else {
Object obj = this.parse();
if (obj == null) {
value = null;
} else {
value = obj.toString();
}
}
array.add(value);
} else {
Object val;
if (lexer.token() == JSONToken.NULL) {
lexer.nextToken();
val = null;
} else {
val = deserializer.deserialze(this, type, i);
}
array.add(val);
checkListResolve(array);
}
if (lexer.token() == JSONToken.COMMA) {
lexer.nextToken(deserializer.getFastMatchToken());
continue;
}
}
} finally {
this.setContext(context);
}
lexer.nextToken(JSONToken.COMMA);
}
public Object[] parseArray(Type[] types) {
if (lexer.token() == JSONToken.NULL) {
lexer.nextToken(JSONToken.COMMA);
return null;
}
if (lexer.token() != JSONToken.LBRACKET) {
throw new JSONException("syntax error : " + lexer.tokenName());
}
Object[] list = new Object[types.length];
if (types.length == 0) {
lexer.nextToken(JSONToken.RBRACKET);
if (lexer.token() != JSONToken.RBRACKET) {
throw new JSONException("syntax error");
}
lexer.nextToken(JSONToken.COMMA);
return new Object[0];
}
lexer.nextToken(JSONToken.LITERAL_INT);
for (int i = 0; i < types.length; ++i) {
Object value;
if (lexer.token() == JSONToken.NULL) {
value = null;
lexer.nextToken(JSONToken.COMMA);
} else {
Type type = types[i];
if (type == int.class || type == Integer.class) {
if (lexer.token() == JSONToken.LITERAL_INT) {
value = Integer.valueOf(lexer.intValue());
lexer.nextToken(JSONToken.COMMA);
} else {
value = this.parse();
value = TypeUtils.cast(value, type, config);
}
} else if (type == String.class) {
if (lexer.token() == JSONToken.LITERAL_STRING) {
value = lexer.stringVal();
lexer.nextToken(JSONToken.COMMA);
} else {
value = this.parse();
value = TypeUtils.cast(value, type, config);
}
} else {
boolean isArray = false;
Class> componentType = null;
if (i == types.length - 1) {
if (type instanceof Class) {
Class> clazz = (Class>) type;
//如果最后一个type是字节数组,且当前token为字符串类型,不应该当作可变长参数进行处理
//而是作为一个整体的Base64字符串进行反序列化
if (!((clazz == byte[].class || clazz == char[].class) && lexer.token() == LITERAL_STRING)) {
isArray = clazz.isArray();
componentType = clazz.getComponentType();
}
}
}
// support varArgs
if (isArray && lexer.token() != JSONToken.LBRACKET) {
List varList = new ArrayList();
ObjectDeserializer deserializer = config.getDeserializer(componentType);
int fastMatch = deserializer.getFastMatchToken();
if (lexer.token() != JSONToken.RBRACKET) {
for (;;) {
Object item = deserializer.deserialze(this, type, null);
varList.add(item);
if (lexer.token() == JSONToken.COMMA) {
lexer.nextToken(fastMatch);
} else if (lexer.token() == JSONToken.RBRACKET) {
break;
} else {
throw new JSONException("syntax error :" + JSONToken.name(lexer.token()));
}
}
}
value = TypeUtils.cast(varList, type, config);
} else {
ObjectDeserializer deserializer = config.getDeserializer(type);
value = deserializer.deserialze(this, type, i);
}
}
}
list[i] = value;
if (lexer.token() == JSONToken.RBRACKET) {
break;
}
if (lexer.token() != JSONToken.COMMA) {
throw new JSONException("syntax error :" + JSONToken.name(lexer.token()));
}
if (i == types.length - 1) {
lexer.nextToken(JSONToken.RBRACKET);
} else {
lexer.nextToken(JSONToken.LITERAL_INT);
}
}
if (lexer.token() != JSONToken.RBRACKET) {
throw new JSONException("syntax error");
}
lexer.nextToken(JSONToken.COMMA);
return list;
}
public void parseObject(Object object) {
Class> clazz = object.getClass();
JavaBeanDeserializer beanDeser = null;
ObjectDeserializer deserializer = config.getDeserializer(clazz);
if (deserializer instanceof JavaBeanDeserializer) {
beanDeser = (JavaBeanDeserializer) deserializer;
}
if (lexer.token() != JSONToken.LBRACE && lexer.token() != JSONToken.COMMA) {
throw new JSONException("syntax error, expect {, actual " + lexer.tokenName());
}
for (;;) {
// lexer.scanSymbol
String key = lexer.scanSymbol(symbolTable);
if (key == null) {
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken(JSONToken.COMMA);
break;
}
if (lexer.token() == JSONToken.COMMA) {
if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
continue;
}
}
}
FieldDeserializer fieldDeser = null;
if (beanDeser != null) {
fieldDeser = beanDeser.getFieldDeserializer(key);
}
if (fieldDeser == null) {
if (!lexer.isEnabled(Feature.IgnoreNotMatch)) {
throw new JSONException("setter not found, class " + clazz.getName() + ", property " + key);
}
lexer.nextTokenWithColon();
parse(); // skip
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
return;
}
continue;
} else {
Class> fieldClass = fieldDeser.fieldInfo.fieldClass;
Type fieldType = fieldDeser.fieldInfo.fieldType;
Object fieldValue;
if (fieldClass == int.class) {
lexer.nextTokenWithColon(JSONToken.LITERAL_INT);
fieldValue = IntegerCodec.instance.deserialze(this, fieldType, null);
} else if (fieldClass == String.class) {
lexer.nextTokenWithColon(JSONToken.LITERAL_STRING);
fieldValue = StringCodec.deserialze(this);
} else if (fieldClass == long.class) {
lexer.nextTokenWithColon(JSONToken.LITERAL_INT);
fieldValue = LongCodec.instance.deserialze(this, fieldType, null);
} else {
ObjectDeserializer fieldValueDeserializer = config.getDeserializer(fieldClass, fieldType);
lexer.nextTokenWithColon(fieldValueDeserializer.getFastMatchToken());
fieldValue = fieldValueDeserializer.deserialze(this, fieldType, null);
}
fieldDeser.setValue(object, fieldValue);
}
if (lexer.token() == JSONToken.COMMA) {
continue;
}
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken(JSONToken.COMMA);
return;
}
}
}
public Object parseArrayWithType(Type collectionType) {
if (lexer.token() == JSONToken.NULL) {
lexer.nextToken();
return null;
}
Type[] actualTypes = ((ParameterizedType) collectionType).getActualTypeArguments();
if (actualTypes.length != 1) {
throw new JSONException("not support type " + collectionType);
}
Type actualTypeArgument = actualTypes[0];
if (actualTypeArgument instanceof Class) {
List array = new ArrayList();
this.parseArray((Class>) actualTypeArgument, array);
return array;
}
if (actualTypeArgument instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) actualTypeArgument;
// assert wildcardType.getUpperBounds().length == 1;
Type upperBoundType = wildcardType.getUpperBounds()[0];
// assert upperBoundType instanceof Class;
if (Object.class.equals(upperBoundType)) {
if (wildcardType.getLowerBounds().length == 0) {
// Collection>
return parse();
} else {
throw new JSONException("not support type : " + collectionType);
}
}
List array = new ArrayList();
this.parseArray((Class>) upperBoundType, array);
return array;
// throw new JSONException("not support type : " +
// collectionType);return parse();
}
if (actualTypeArgument instanceof TypeVariable) {
TypeVariable> typeVariable = (TypeVariable>) actualTypeArgument;
Type[] bounds = typeVariable.getBounds();
if (bounds.length != 1) {
throw new JSONException("not support : " + typeVariable);
}
Type boundType = bounds[0];
if (boundType instanceof Class) {
List array = new ArrayList();
this.parseArray((Class>) boundType, array);
return array;
}
}
if (actualTypeArgument instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) actualTypeArgument;
List array = new ArrayList();
this.parseArray(parameterizedType, array);
return array;
}
throw new JSONException("TODO : " + collectionType);
}
public void acceptType(String typeName) {
JSONLexer lexer = this.lexer;
lexer.nextTokenWithColon();
if (lexer.token() != JSONToken.LITERAL_STRING) {
throw new JSONException("type not match error");
}
if (typeName.equals(lexer.stringVal())) {
lexer.nextToken();
if (lexer.token() == JSONToken.COMMA) {
lexer.nextToken();
}
} else {
throw new JSONException("type not match error");
}
}
public int getResolveStatus() {
return resolveStatus;
}
public void setResolveStatus(int resolveStatus) {
this.resolveStatus = resolveStatus;
}
public Object getObject(String path) {
for (int i = 0; i < contextArrayIndex; ++i) {
if (path.equals(contextArray[i].toString())) {
return contextArray[i].object;
}
}
return null;
}
@SuppressWarnings("rawtypes")
public void checkListResolve(Collection array) {
if (resolveStatus == NeedToResolve) {
if (array instanceof List) {
final int index = array.size() - 1;
final List list = (List) array;
ResolveTask task = getLastResolveTask();
task.fieldDeserializer = new ResolveFieldDeserializer(this, list, index);
task.ownerContext = context;
setResolveStatus(DefaultJSONParser.NONE);
} else {
ResolveTask task = getLastResolveTask();
task.fieldDeserializer = new ResolveFieldDeserializer(array);
task.ownerContext = context;
setResolveStatus(DefaultJSONParser.NONE);
}
}
}
@SuppressWarnings("rawtypes")
public void checkMapResolve(Map object, Object fieldName) {
if (resolveStatus == NeedToResolve) {
ResolveFieldDeserializer fieldResolver = new ResolveFieldDeserializer(object, fieldName);
ResolveTask task = getLastResolveTask();
task.fieldDeserializer = fieldResolver;
task.ownerContext = context;
setResolveStatus(DefaultJSONParser.NONE);
}
}
@SuppressWarnings("rawtypes")
public Object parseObject(final Map object) {
return parseObject(object, null);
}
public JSONObject parseObject() {
JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
Object parsedObject = parseObject(object);
if (parsedObject instanceof JSONObject) {
return (JSONObject) parsedObject;
}
if (parsedObject == null) {
return null;
}
return new JSONObject((Map) parsedObject);
}
@SuppressWarnings("rawtypes")
public final void parseArray(final Collection array) {
parseArray(array, null);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public final void parseArray(final Collection array, Object fieldName) {
final JSONLexer lexer = this.lexer;
if (lexer.token() == JSONToken.SET || lexer.token() == JSONToken.TREE_SET) {
lexer.nextToken();
}
if (lexer.token() != JSONToken.LBRACKET) {
throw new JSONException("syntax error, expect [, actual " + JSONToken.name(lexer.token()) + ", pos "
+ lexer.pos() + ", fieldName " + fieldName);
}
lexer.nextToken(JSONToken.LITERAL_STRING);
if (this.context != null && this.context.level > 512) {
throw new JSONException("array level > 512");
}
ParseContext context = this.context;
this.setContext(array, fieldName);
try {
for (int i = 0; ; ++i) {
if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
while (lexer.token() == JSONToken.COMMA) {
lexer.nextToken();
continue;
}
}
Object value;
switch (lexer.token()) {
case LITERAL_INT:
value = lexer.integerValue();
lexer.nextToken(JSONToken.COMMA);
break;
case LITERAL_FLOAT:
if (lexer.isEnabled(Feature.UseBigDecimal)) {
value = lexer.decimalValue(true);
} else {
value = lexer.decimalValue(false);
}
lexer.nextToken(JSONToken.COMMA);
break;
case LITERAL_STRING:
String stringLiteral = lexer.stringVal();
lexer.nextToken(JSONToken.COMMA);
if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {
JSONScanner iso8601Lexer = new JSONScanner(stringLiteral);
if (iso8601Lexer.scanISO8601DateIfMatch()) {
value = iso8601Lexer.getCalendar().getTime();
} else {
value = stringLiteral;
}
iso8601Lexer.close();
} else {
value = stringLiteral;
}
break;
case TRUE:
value = Boolean.TRUE;
lexer.nextToken(JSONToken.COMMA);
break;
case FALSE:
value = Boolean.FALSE;
lexer.nextToken(JSONToken.COMMA);
break;
case LBRACE:
JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
value = parseObject(object, i);
break;
case LBRACKET:
Collection items = new JSONArray();
parseArray(items, i);
if (lexer.isEnabled(Feature.UseObjectArray)) {
value = items.toArray();
} else {
value = items;
}
break;
case NULL:
value = null;
lexer.nextToken(JSONToken.LITERAL_STRING);
break;
case UNDEFINED:
value = null;
lexer.nextToken(JSONToken.LITERAL_STRING);
break;
case RBRACKET:
lexer.nextToken(JSONToken.COMMA);
return;
case EOF:
throw new JSONException("unclosed jsonArray");
default:
value = parse();
break;
}
array.add(value);
checkListResolve(array);
if (lexer.token() == JSONToken.COMMA) {
lexer.nextToken(JSONToken.LITERAL_STRING);
continue;
}
}
} catch (ClassCastException e) {
throw new JSONException("unkown error", e);
} finally {
this.setContext(context);
}
}
public ParseContext getContext() {
return context;
}
public ParseContext getOwnerContext() {
return context.parent;
}
public List getResolveTaskList() {
if (resolveTaskList == null) {
resolveTaskList = new ArrayList(2);
}
return resolveTaskList;
}
public void addResolveTask(ResolveTask task) {
if (resolveTaskList == null) {
resolveTaskList = new ArrayList(2);
}
resolveTaskList.add(task);
}
public ResolveTask getLastResolveTask() {
return resolveTaskList.get(resolveTaskList.size() - 1);
}
public List getExtraProcessors() {
if (extraProcessors == null) {
extraProcessors = new ArrayList(2);
}
return extraProcessors;
}
public List getExtraTypeProviders() {
if (extraTypeProviders == null) {
extraTypeProviders = new ArrayList(2);
}
return extraTypeProviders;
}
public FieldTypeResolver getFieldTypeResolver() {
return fieldTypeResolver;
}
public void setFieldTypeResolver(FieldTypeResolver fieldTypeResolver) {
this.fieldTypeResolver = fieldTypeResolver;
}
public void setContext(ParseContext context) {
if (lexer.isEnabled(Feature.DisableCircularReferenceDetect)) {
return;
}
this.context = context;
}
public void popContext() {
if (lexer.isEnabled(Feature.DisableCircularReferenceDetect)) {
return;
}
this.context = this.context.parent;
if (contextArrayIndex <= 0) {
return;
}
contextArrayIndex--;
contextArray[contextArrayIndex] = null;
}
public ParseContext setContext(Object object, Object fieldName) {
if (lexer.isEnabled(Feature.DisableCircularReferenceDetect)) {
return null;
}
return setContext(this.context, object, fieldName);
}
public ParseContext setContext(ParseContext parent, Object object, Object fieldName) {
if (lexer.isEnabled(Feature.DisableCircularReferenceDetect)) {
return null;
}
this.context = new ParseContext(parent, object, fieldName);
addContext(this.context);
return this.context;
}
private void addContext(ParseContext context) {
int i = contextArrayIndex++;
if (contextArray == null) {
contextArray = new ParseContext[8];
} else if (i >= contextArray.length) {
int newLen = (contextArray.length * 3) / 2;
ParseContext[] newArray = new ParseContext[newLen];
System.arraycopy(contextArray, 0, newArray, 0, contextArray.length);
contextArray = newArray;
}
contextArray[i] = context;
}
public Object parse() {
return parse(null);
}
public Object parseKey() {
if (lexer.token() == JSONToken.IDENTIFIER) {
String value = lexer.stringVal();
lexer.nextToken(JSONToken.COMMA);
return value;
}
return parse(null);
}
public Object parse(Object fieldName) {
final JSONLexer lexer = this.lexer;
switch (lexer.token()) {
case SET:
lexer.nextToken();
HashSet set = new HashSet();
parseArray(set, fieldName);
return set;
case TREE_SET:
lexer.nextToken();
TreeSet treeSet = new TreeSet();
parseArray(treeSet, fieldName);
return treeSet;
case LBRACKET:
Collection array = isEnabled(Feature.UseNativeJavaObject)
? new ArrayList()
: new JSONArray();
parseArray(array, fieldName);
if (lexer.isEnabled(Feature.UseObjectArray)) {
return array.toArray();
}
return array;
case LBRACE:
Map object = isEnabled(Feature.UseNativeJavaObject)
? lexer.isEnabled(Feature.OrderedField)
? new HashMap()
: new LinkedHashMap()
: new JSONObject(lexer.isEnabled(Feature.OrderedField));
return parseObject(object, fieldName);
// case LBRACE: {
// Map map = lexer.isEnabled(Feature.OrderedField)
// ? new LinkedHashMap()
// : new HashMap();
// Object obj = parseObject(map, fieldName);
// if (obj != map) {
// return obj;
// }
// return new JSONObject(map);
// }
case LITERAL_INT:
Number intValue = lexer.integerValue();
lexer.nextToken();
return intValue;
case LITERAL_FLOAT:
Object value = lexer.decimalValue(lexer.isEnabled(Feature.UseBigDecimal));
lexer.nextToken();
return value;
case LITERAL_STRING:
String stringLiteral = lexer.stringVal();
lexer.nextToken(JSONToken.COMMA);
if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {
JSONScanner iso8601Lexer = new JSONScanner(stringLiteral);
try {
if (iso8601Lexer.scanISO8601DateIfMatch()) {
return iso8601Lexer.getCalendar().getTime();
}
} finally {
iso8601Lexer.close();
}
}
return stringLiteral;
case NULL:
lexer.nextToken();
return null;
case UNDEFINED:
lexer.nextToken();
return null;
case TRUE:
lexer.nextToken();
return Boolean.TRUE;
case FALSE:
lexer.nextToken();
return Boolean.FALSE;
case NEW:
lexer.nextToken(JSONToken.IDENTIFIER);
if (lexer.token() != JSONToken.IDENTIFIER) {
throw new JSONException("syntax error");
}
lexer.nextToken(JSONToken.LPAREN);
accept(JSONToken.LPAREN);
long time = lexer.integerValue().longValue();
accept(JSONToken.LITERAL_INT);
accept(JSONToken.RPAREN);
return new Date(time);
case EOF:
if (lexer.isBlankInput()) {
return null;
}
throw new JSONException("unterminated json string, " + lexer.info());
case HEX:
byte[] bytes = lexer.bytesValue();
lexer.nextToken();
return bytes;
case IDENTIFIER:
String identifier = lexer.stringVal();
if ("NaN".equals(identifier)) {
lexer.nextToken();
return null;
}
throw new JSONException("syntax error, " + lexer.info());
case ERROR:
default:
throw new JSONException("syntax error, " + lexer.info());
}
}
public void config(Feature feature, boolean state) {
this.lexer.config(feature, state);
}
public boolean isEnabled(Feature feature) {
return lexer.isEnabled(feature);
}
public JSONLexer getLexer() {
return lexer;
}
public final void accept(final int token) {
final JSONLexer lexer = this.lexer;
if (lexer.token() == token) {
lexer.nextToken();
} else {
throw new JSONException("syntax error, expect " + JSONToken.name(token) + ", actual "
+ JSONToken.name(lexer.token()));
}
}
public final void accept(final int token, int nextExpectToken) {
final JSONLexer lexer = this.lexer;
if (lexer.token() == token) {
lexer.nextToken(nextExpectToken);
} else {
throwException(token);
}
}
public void throwException(int token) {
throw new JSONException("syntax error, expect " + JSONToken.name(token) + ", actual "
+ JSONToken.name(lexer.token()));
}
public void close() {
final JSONLexer lexer = this.lexer;
try {
if (lexer.isEnabled(Feature.AutoCloseSource)) {
if (lexer.token() != JSONToken.EOF) {
throw new JSONException("not close json text, token : " + JSONToken.name(lexer.token()));
}
}
} finally {
lexer.close();
}
}
public Object resolveReference(String ref) {
if(contextArray == null) {
return null;
}
for (int i = 0; i < contextArray.length && i < contextArrayIndex; i++) {
ParseContext context = contextArray[i];
if (context.toString().equals(ref)) {
return context.object;
}
}
return null;
}
public void handleResovleTask(Object value) {
if (resolveTaskList == null) {
return;
}
for (int i = 0, size = resolveTaskList.size(); i < size; ++i) {
ResolveTask task = resolveTaskList.get(i);
String ref = task.referenceValue;
Object object = null;
if (task.ownerContext != null) {
object = task.ownerContext.object;
}
Object refValue;
if (ref.startsWith("$")) {
refValue = getObject(ref);
if (refValue == null) {
try {
JSONPath jsonpath = new JSONPath(ref, SerializeConfig.getGlobalInstance(), config, true);
if (jsonpath.isRef()) {
refValue = jsonpath.eval(value);
}
} catch (JSONPathException ex) {
// skip
}
}
} else {
refValue = task.context.object;
}
FieldDeserializer fieldDeser = task.fieldDeserializer;
if (fieldDeser != null) {
if (refValue != null
&& refValue.getClass() == JSONObject.class
&& fieldDeser.fieldInfo != null
&& !Map.class.isAssignableFrom(fieldDeser.fieldInfo.fieldClass)) {
Object root = this.contextArray[0].object;
JSONPath jsonpath = JSONPath.compile(ref);
if (jsonpath.isRef()) {
refValue = jsonpath.eval(root);
}
}
// workaround for bug
if (fieldDeser.getOwnerClass() != null
&& (!fieldDeser.getOwnerClass().isInstance(object))
&& task.ownerContext.parent != null
) {
for (ParseContext ctx = task.ownerContext.parent;ctx != null;ctx = ctx.parent) {
if (fieldDeser.getOwnerClass().isInstance(ctx.object)) {
object = ctx.object;
break;
}
}
}
fieldDeser.setValue(object, refValue);
}
}
}
public static class ResolveTask {
public final ParseContext context;
public final String referenceValue;
public FieldDeserializer fieldDeserializer;
public ParseContext ownerContext;
public ResolveTask(ParseContext context, String referenceValue){
this.context = context;
this.referenceValue = referenceValue;
}
}
public void parseExtra(Object object, String key) {
final JSONLexer lexer = this.lexer; // xxx
lexer.nextTokenWithColon();
Type type = null;
if (extraTypeProviders != null) {
for (ExtraTypeProvider extraProvider : extraTypeProviders) {
type = extraProvider.getExtraType(object, key);
}
}
Object value = type == null //
? parse() // skip
: parseObject(type);
if (object instanceof ExtraProcessable) {
ExtraProcessable extraProcessable = ((ExtraProcessable) object);
extraProcessable.processExtra(key, value);
return;
}
if (extraProcessors != null) {
for (ExtraProcessor process : extraProcessors) {
process.processExtra(object, key, value);
}
}
if (resolveStatus == NeedToResolve) {
resolveStatus = NONE;
}
}
public Object parse(PropertyProcessable object, Object fieldName) {
if (lexer.token() != JSONToken.LBRACE) {
String msg = "syntax error, expect {, actual " + lexer.tokenName();
if (fieldName instanceof String) {
msg += ", fieldName ";
msg += fieldName;
}
msg += ", ";
msg += lexer.info();
JSONArray array = new JSONArray();
parseArray(array, fieldName);
if (array.size() == 1) {
Object first = array.get(0);
if (first instanceof JSONObject) {
return (JSONObject) first;
}
}
throw new JSONException(msg);
}
ParseContext context = this.context;
try {
for (int i = 0;;++i) {
lexer.skipWhitespace();
char ch = lexer.getCurrent();
if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
while (ch == ',') {
lexer.next();
lexer.skipWhitespace();
ch = lexer.getCurrent();
}
}
String key;
if (ch == '"') {
key = lexer.scanSymbol(symbolTable, '"');
lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("expect ':' at " + lexer.pos());
}
} else if (ch == '}') {
lexer.next();
lexer.resetStringPosition();
lexer.nextToken(JSONToken.COMMA);
return object;
} else if (ch == '\'') {
if (!lexer.isEnabled(Feature.AllowSingleQuotes)) {
throw new JSONException("syntax error");
}
key = lexer.scanSymbol(symbolTable, '\'');
lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("expect ':' at " + lexer.pos());
}
} else {
if (!lexer.isEnabled(Feature.AllowUnQuotedFieldNames)) {
throw new JSONException("syntax error");
}
key = lexer.scanSymbolUnQuoted(symbolTable);
lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("expect ':' at " + lexer.pos() + ", actual " + ch);
}
}
lexer.next();
lexer.skipWhitespace();
ch = lexer.getCurrent();
lexer.resetStringPosition();
if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
String typeName = lexer.scanSymbol(symbolTable, '"');
Class> clazz = config.checkAutoType(typeName, null, lexer.getFeatures());
if (Map.class.isAssignableFrom(clazz) ) {
lexer.nextToken(JSONToken.COMMA);
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken(JSONToken.COMMA);
return object;
}
continue;
}
ObjectDeserializer deserializer = config.getDeserializer(clazz);
lexer.nextToken(JSONToken.COMMA);
setResolveStatus(DefaultJSONParser.TypeNameRedirect);
if (context != null && !(fieldName instanceof Integer)) {
popContext();
}
return (Map) deserializer.deserialze(this, clazz, fieldName);
}
Object value;
lexer.nextToken();
if (i != 0) {
setContext(context);
}
Type valueType = object.getType(key);
if (lexer.token() == JSONToken.NULL) {
value = null;
lexer.nextToken();
} else {
value = parseObject(valueType, key);
}
object.apply(key, value);
setContext(context, value, key);
setContext(context);
final int tok = lexer.token();
if (tok == JSONToken.EOF || tok == JSONToken.RBRACKET) {
return object;
}
if (tok == JSONToken.RBRACE) {
lexer.nextToken();
return object;
}
}
} finally {
setContext(context);
}
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/Feature.java
================================================
/*
* Copyright 1999-2017 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.fastjson.parser;
/**
* @author wenshao[szujobs@hotmail.com]
*/
public enum Feature {
/**
*
*/
AutoCloseSource,
/**
*
*/
AllowComment,
/**
*
*/
AllowUnQuotedFieldNames,
/**
*
*/
AllowSingleQuotes,
/**
*
*/
InternFieldNames,
/**
*
*/
AllowISO8601DateFormat,
/**
* {"a":1,,,"b":2}
*/
AllowArbitraryCommas,
/**
*
*/
UseBigDecimal,
/**
* @since 1.1.2
*/
IgnoreNotMatch,
/**
* @since 1.1.3
*/
SortFeidFastMatch,
/**
* @since 1.1.3
*/
DisableASM,
/**
* @since 1.1.7
*/
DisableCircularReferenceDetect,
/**
* @since 1.1.10
*/
InitStringFieldAsEmpty,
/**
* @since 1.1.35
*
*/
SupportArrayToBean,
/**
* @since 1.2.3
*
*/
OrderedField,
/**
* @since 1.2.5
*
*/
DisableSpecialKeyDetect,
/**
* @since 1.2.9
*/
UseObjectArray,
/**
* @since 1.2.22, 1.1.54.android
*/
SupportNonPublicField,
/**
* @since 1.2.29
*
* disable autotype key '@type'
*/
IgnoreAutoType,
/**
* @since 1.2.30
*
* disable field smart match, improve performance in some scenarios.
*/
DisableFieldSmartMatch,
/**
* @since 1.2.41, backport to 1.1.66.android
*/
SupportAutoType,
/**
* @since 1.2.42
*/
NonStringKeyAsString,
/**
* @since 1.2.45
*/
CustomMapDeserializer,
/**
* @since 1.2.55
*/
ErrorOnEnumNotMatch,
/**
* @since 1.2.68
*/
SafeMode,
/**
* @since 1.2.72
*/
TrimStringFieldValue,
/**
* @since 1.2.77
* use HashMap instead of JSONObject, ArrayList instead of JSONArray
*/
UseNativeJavaObject
;
Feature(){
mask = (1 << ordinal());
}
public final int mask;
public final int getMask() {
return mask;
}
public static boolean isEnabled(int features, Feature feature) {
return (features & feature.mask) != 0;
}
public static int config(int features, Feature feature, boolean state) {
if (state) {
features |= feature.mask;
} else {
features &= ~feature.mask;
}
return features;
}
public static int of(Feature[] features) {
if (features == null) {
return 0;
}
int value = 0;
for (Feature feature: features) {
value |= feature.mask;
}
return value;
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/JSONLexer.java
================================================
/*
* Copyright 1999-2019 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.fastjson.parser;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Locale;
import java.util.TimeZone;
public interface JSONLexer {
char EOI = 0x1A;
int NOT_MATCH = -1;
int NOT_MATCH_NAME = -2;
int UNKNOWN = 0;
int OBJECT = 1;
int ARRAY = 2;
int VALUE = 3;
int END = 4;
int VALUE_NULL = 5;
int token();
String tokenName();
void skipWhitespace();
void nextToken();
void nextToken(int expect);
char getCurrent();
char next();
String scanSymbol(final SymbolTable symbolTable);
String scanSymbol(final SymbolTable symbolTable, final char quote);
void resetStringPosition();
void scanNumber();
int pos();
Number integerValue();
BigDecimal decimalValue();
Number decimalValue(boolean decimal);
String scanSymbolUnQuoted(final SymbolTable symbolTable);
String stringVal();
boolean isEnabled(int feature);
boolean isEnabled(Feature feature);
void config(Feature feature, boolean state);
void scanString();
int intValue();
void nextTokenWithColon();
void nextTokenWithColon(int expect);
boolean isBlankInput();
void close();
long longValue();
boolean isRef();
String scanTypeName(SymbolTable symbolTable);
String numberString();
byte[] bytesValue();
float floatValue();
int scanInt(char expectNext);
long scanLong(char expectNextChar);
float scanFloat(char seperator);
double scanDouble(char seperator);
boolean scanBoolean(char expectNext);
BigDecimal scanDecimal(char seperator);
String scanString(char expectNextChar);
Enum> scanEnum(Class> enumClass, final SymbolTable symbolTable, char serperator);
String scanSymbolWithSeperator(final SymbolTable symbolTable, char serperator);
void scanStringArray(Collection collection, char seperator);
TimeZone getTimeZone();
void setTimeZone(TimeZone timeZone);
Locale getLocale();
void setLocale(Locale locale);
String info();
int getFeatures();
void setFeatures(int features);
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/JSONLexerBase.java
================================================
/*
* Copyright 1999-2019 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.fastjson.parser;
import java.io.Closeable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.util.IOUtils;
import static com.alibaba.fastjson.parser.JSONToken.*;
import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_hashcode;
import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_prime;
/**
* @author wenshao[szujobs@hotmail.com]
*/
public abstract class JSONLexerBase implements JSONLexer, Closeable {
protected void lexError(String key, Object... args) {
token = ERROR;
}
protected int token;
protected int pos;
protected int features;
protected char ch;
protected int bp;
protected int eofPos;
/**
* A character buffer for literals.
*/
protected char[] sbuf;
protected int sp;
/**
* number start position
*/
protected int np;
protected boolean hasSpecial;
protected Calendar calendar = null;
protected TimeZone timeZone = JSON.defaultTimeZone;
protected Locale locale = JSON.defaultLocale;
public int matchStat = UNKNOWN;
private final static ThreadLocal SBUF_LOCAL = new ThreadLocal();
protected String stringDefaultValue = null;
protected int nanos = 0;
public JSONLexerBase(int features){
this.features = features;
if ((features & Feature.InitStringFieldAsEmpty.mask) != 0) {
stringDefaultValue = "";
}
sbuf = SBUF_LOCAL.get();
if (sbuf == null) {
sbuf = new char[512];
}
}
public final int matchStat() {
return matchStat;
}
/**
* internal method, don't invoke
* @param token
*/
public void setToken(int token) {
this.token = token;
}
public final void nextToken() {
sp = 0;
for (;;) {
pos = bp;
if (ch == '/') {
skipComment();
continue;
}
if (ch == '"') {
scanString();
return;
}
if (ch == ',') {
next();
token = COMMA;
return;
}
if (ch >= '0' && ch <= '9') {
scanNumber();
return;
}
if (ch == '-') {
scanNumber();
return;
}
switch (ch) {
case '\'':
if (!isEnabled(Feature.AllowSingleQuotes)) {
throw new JSONException("Feature.AllowSingleQuotes is false");
}
scanStringSingleQuote();
return;
case ' ':
case '\t':
case '\b':
case '\f':
case '\n':
case '\r':
next();
break;
case 't': // true
scanTrue();
return;
case 'f': // false
scanFalse();
return;
case 'n': // new,null
scanNullOrNew();
return;
case 'T':
case 'N': // NULL
case 'S':
case 'u': // undefined
scanIdent();
return;
case '(':
next();
token = LPAREN;
return;
case ')':
next();
token = RPAREN;
return;
case '[':
next();
token = LBRACKET;
return;
case ']':
next();
token = RBRACKET;
return;
case '{':
next();
token = LBRACE;
return;
case '}':
next();
token = RBRACE;
return;
case ':':
next();
token = COLON;
return;
case ';':
next();
token = SEMI;
return;
case '.':
next();
token = DOT;
return;
case '+':
next();
scanNumber();
return;
case 'x':
scanHex();
return;
default:
if (isEOF()) { // JLS
if (token == EOF) {
throw new JSONException("EOF error");
}
token = EOF;
eofPos = pos = bp;
} else {
if (ch <= 31 || ch == 127) {
next();
break;
}
lexError("illegal.char", String.valueOf((int) ch));
next();
}
return;
}
}
}
public final void nextToken(int expect) {
sp = 0;
for (;;) {
switch (expect) {
case JSONToken.LBRACE:
if (ch == '{') {
token = JSONToken.LBRACE;
next();
return;
}
if (ch == '[') {
token = JSONToken.LBRACKET;
next();
return;
}
break;
case JSONToken.COMMA:
if (ch == ',') {
token = JSONToken.COMMA;
next();
return;
}
if (ch == '}') {
token = JSONToken.RBRACE;
next();
return;
}
if (ch == ']') {
token = JSONToken.RBRACKET;
next();
return;
}
if (ch == EOI) {
token = JSONToken.EOF;
return;
}
if (ch == 'n') {
scanNullOrNew(false);
return;
}
break;
case JSONToken.LITERAL_INT:
if (ch >= '0' && ch <= '9') {
pos = bp;
scanNumber();
return;
}
if (ch == '"') {
pos = bp;
scanString();
return;
}
if (ch == '[') {
token = JSONToken.LBRACKET;
next();
return;
}
if (ch == '{') {
token = JSONToken.LBRACE;
next();
return;
}
break;
case JSONToken.LITERAL_STRING:
if (ch == '"') {
pos = bp;
scanString();
return;
}
if (ch >= '0' && ch <= '9') {
pos = bp;
scanNumber();
return;
}
if (ch == '[') {
token = JSONToken.LBRACKET;
next();
return;
}
if (ch == '{') {
token = JSONToken.LBRACE;
next();
return;
}
break;
case JSONToken.LBRACKET:
if (ch == '[') {
token = JSONToken.LBRACKET;
next();
return;
}
if (ch == '{') {
token = JSONToken.LBRACE;
next();
return;
}
break;
case JSONToken.RBRACKET:
if (ch == ']') {
token = JSONToken.RBRACKET;
next();
return;
}
case JSONToken.EOF:
if (ch == EOI) {
token = JSONToken.EOF;
return;
}
break;
case JSONToken.IDENTIFIER:
nextIdent();
return;
default:
break;
}
if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' || ch == '\b') {
next();
continue;
}
nextToken();
break;
}
}
public final void nextIdent() {
while (isWhitespace(ch)) {
next();
}
if (ch == '_' || ch == '$' || Character.isLetter(ch)) {
scanIdent();
} else {
nextToken();
}
}
public final void nextTokenWithColon() {
nextTokenWithChar(':');
}
public final void nextTokenWithChar(char expect) {
sp = 0;
for (;;) {
if (ch == expect) {
next();
nextToken();
return;
}
if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' || ch == '\b') {
next();
continue;
}
throw new JSONException("not match " + expect + " - " + ch + ", info : " + this.info());
}
}
public final int token() {
return token;
}
public final String tokenName() {
return JSONToken.name(token);
}
public final int pos() {
return pos;
}
public final String stringDefaultValue() {
return stringDefaultValue;
}
public final Number integerValue() throws NumberFormatException {
long result = 0;
boolean negative = false;
if (np == -1) {
np = 0;
}
int i = np, max = np + sp;
long limit;
long multmin;
int digit;
char type = ' ';
switch (charAt(max - 1)) {
case 'L':
max--;
type = 'L';
break;
case 'S':
max--;
type = 'S';
break;
case 'B':
max--;
type = 'B';
break;
default:
break;
}
if (charAt(np) == '-') {
negative = true;
limit = Long.MIN_VALUE;
i++;
} else {
limit = -Long.MAX_VALUE;
}
multmin = MULTMIN_RADIX_TEN;
if (i < max) {
digit = charAt(i++) - '0';
result = -digit;
}
while (i < max) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = charAt(i++) - '0';
if (result < multmin) {
return new BigInteger(numberString(), 10);
}
result *= 10;
if (result < limit + digit) {
return new BigInteger(numberString(), 10);
}
result -= digit;
}
if (negative) {
if (i > np + 1) {
if (result >= Integer.MIN_VALUE && type != 'L') {
if (type == 'S') {
return (short) result;
}
if (type == 'B') {
return (byte) result;
}
return (int) result;
}
return result;
} else { /* Only got "-" */
throw new JSONException("illegal number format : " + numberString());
}
} else {
result = -result;
if (result <= Integer.MAX_VALUE && type != 'L') {
if (type == 'S') {
return (short) result;
}
if (type == 'B') {
return (byte) result;
}
return (int) result;
}
return result;
}
}
public final void nextTokenWithColon(int expect) {
nextTokenWithChar(':');
}
public float floatValue() {
String strVal = numberString();
float floatValue = Float.parseFloat(strVal);
if (floatValue == 0 || floatValue == Float.POSITIVE_INFINITY) {
char c0 = strVal.charAt(0);
if (c0 > '0' && c0 <= '9') {
throw new JSONException("float overflow : " + strVal);
}
}
return floatValue;
}
public double doubleValue() {
return Double.parseDouble(numberString());
}
public void config(Feature feature, boolean state) {
features = Feature.config(features, feature, state);
if ((features & Feature.InitStringFieldAsEmpty.mask) != 0) {
stringDefaultValue = "";
}
}
public final boolean isEnabled(Feature feature) {
return isEnabled(feature.mask);
}
public final boolean isEnabled(int feature) {
return (this.features & feature) != 0;
}
public final boolean isEnabled(int features, int feature) {
return (this.features & feature) != 0 || (features & feature) != 0;
}
public abstract String numberString();
public abstract boolean isEOF();
public final char getCurrent() {
return ch;
}
public abstract char charAt(int index);
// public final char next() {
// ch = doNext();
//// if (ch == '/' && (this.features & Feature.AllowComment.mask) != 0) {
//// skipComment();
//// }
// return ch;
// }
public abstract char next();
protected void skipComment() {
next();
if (ch == '/') {
for (;;) {
next();
if (ch == '\n') {
next();
return;
} else if (ch == EOI) {
return;
}
}
} else if (ch == '*') {
next();
for (; ch != EOI;) {
if (ch == '*') {
next();
if (ch == '/') {
next();
return;
} else {
continue;
}
}
next();
}
} else {
throw new JSONException("invalid comment");
}
}
public final String scanSymbol(final SymbolTable symbolTable) {
skipWhitespace();
if (ch == '"') {
return scanSymbol(symbolTable, '"');
}
if (ch == '\'') {
if (!isEnabled(Feature.AllowSingleQuotes)) {
throw new JSONException("syntax error");
}
return scanSymbol(symbolTable, '\'');
}
if (ch == '}') {
next();
token = JSONToken.RBRACE;
return null;
}
if (ch == ',') {
next();
token = JSONToken.COMMA;
return null;
}
if (ch == EOI) {
token = JSONToken.EOF;
return null;
}
if (!isEnabled(Feature.AllowUnQuotedFieldNames)) {
throw new JSONException("syntax error");
}
return scanSymbolUnQuoted(symbolTable);
}
// public abstract String scanSymbol(final SymbolTable symbolTable, final char quote);
protected abstract void arrayCopy(int srcPos, char[] dest, int destPos, int length);
public final String scanSymbol(final SymbolTable symbolTable, final char quote) {
int hash = 0;
np = bp;
sp = 0;
boolean hasSpecial = false;
char chLocal;
for (;;) {
chLocal = next();
if (chLocal == quote) {
break;
}
if (chLocal == EOI) {
throw new JSONException("unclosed.str");
}
if (chLocal == '\\') {
if (!hasSpecial) {
hasSpecial = true;
if (sp >= sbuf.length) {
int newCapcity = sbuf.length * 2;
if (sp > newCapcity) {
newCapcity = sp;
}
char[] newsbuf = new char[newCapcity];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
// text.getChars(np + 1, np + 1 + sp, sbuf, 0);
// System.arraycopy(this.buf, np + 1, sbuf, 0, sp);
arrayCopy(np + 1, sbuf, 0, sp);
}
chLocal = next();
switch (chLocal) {
case '0':
hash = 31 * hash + (int) chLocal;
putChar('\0');
break;
case '1':
hash = 31 * hash + (int) chLocal;
putChar('\1');
break;
case '2':
hash = 31 * hash + (int) chLocal;
putChar('\2');
break;
case '3':
hash = 31 * hash + (int) chLocal;
putChar('\3');
break;
case '4':
hash = 31 * hash + (int) chLocal;
putChar('\4');
break;
case '5':
hash = 31 * hash + (int) chLocal;
putChar('\5');
break;
case '6':
hash = 31 * hash + (int) chLocal;
putChar('\6');
break;
case '7':
hash = 31 * hash + (int) chLocal;
putChar('\7');
break;
case 'b': // 8
hash = 31 * hash + (int) '\b';
putChar('\b');
break;
case 't': // 9
hash = 31 * hash + (int) '\t';
putChar('\t');
break;
case 'n': // 10
hash = 31 * hash + (int) '\n';
putChar('\n');
break;
case 'v': // 11
hash = 31 * hash + (int) '\u000B';
putChar('\u000B');
break;
case 'f': // 12
case 'F':
hash = 31 * hash + (int) '\f';
putChar('\f');
break;
case 'r': // 13
hash = 31 * hash + (int) '\r';
putChar('\r');
break;
case '"': // 34
hash = 31 * hash + (int) '"';
putChar('"');
break;
case '\'': // 39
hash = 31 * hash + (int) '\'';
putChar('\'');
break;
case '/': // 47
hash = 31 * hash + (int) '/';
putChar('/');
break;
case '\\': // 92
hash = 31 * hash + (int) '\\';
putChar('\\');
break;
case 'x':
char x1 = ch = next();
char x2 = ch = next();
int x_val = digits[x1] * 16 + digits[x2];
char x_char = (char) x_val;
hash = 31 * hash + (int) x_char;
putChar(x_char);
break;
case 'u':
char c1 = chLocal = next();
char c2 = chLocal = next();
char c3 = chLocal = next();
char c4 = chLocal = next();
int val = Integer.parseInt(new String(new char[] { c1, c2, c3, c4 }), 16);
hash = 31 * hash + val;
putChar((char) val);
break;
default:
this.ch = chLocal;
throw new JSONException("unclosed.str.lit");
}
continue;
}
hash = 31 * hash + chLocal;
if (!hasSpecial) {
sp++;
continue;
}
if (sp == sbuf.length) {
putChar(chLocal);
} else {
sbuf[sp++] = chLocal;
}
}
token = LITERAL_STRING;
String value;
if (!hasSpecial) {
// return this.text.substring(np + 1, np + 1 + sp).intern();
int offset;
if (np == -1) {
offset = 0;
} else {
offset = np + 1;
}
value = addSymbol(offset, sp, hash, symbolTable);
} else {
value = symbolTable.addSymbol(sbuf, 0, sp, hash);
}
sp = 0;
this.next();
return value;
}
public final void resetStringPosition() {
this.sp = 0;
}
public String info() {
return "";
}
public final String scanSymbolUnQuoted(final SymbolTable symbolTable) {
if (token == JSONToken.ERROR && pos == 0 && bp == 1) {
bp = 0; // adjust
}
final boolean[] firstIdentifierFlags = IOUtils.firstIdentifierFlags;
final char first = ch;
final boolean firstFlag = ch >= firstIdentifierFlags.length || firstIdentifierFlags[first];
if (!firstFlag) {
throw new JSONException("illegal identifier : " + ch //
+ info());
}
final boolean[] identifierFlags = IOUtils.identifierFlags;
int hash = first;
np = bp;
sp = 1;
char chLocal;
for (;;) {
chLocal = next();
if (chLocal < identifierFlags.length) {
if (!identifierFlags[chLocal]) {
break;
}
}
hash = 31 * hash + chLocal;
sp++;
continue;
}
this.ch = charAt(bp);
token = JSONToken.IDENTIFIER;
final int NULL_HASH = 3392903;
if (sp == 4 && hash == NULL_HASH && charAt(np) == 'n' && charAt(np + 1) == 'u' && charAt(np + 2) == 'l'
&& charAt(np + 3) == 'l') {
return null;
}
// return text.substring(np, np + sp).intern();
if (symbolTable == null) {
return subString(np, sp);
}
return this.addSymbol(np, sp, hash, symbolTable);
// return symbolTable.addSymbol(buf, np, sp, hash);
}
protected abstract void copyTo(int offset, int count, char[] dest);
public final void scanString() {
np = bp;
hasSpecial = false;
char ch;
for (;;) {
ch = next();
if (ch == '\"') {
break;
}
if (ch == EOI) {
if (!isEOF()) {
putChar((char) EOI);
continue;
}
throw new JSONException("unclosed string : " + ch);
}
if (ch == '\\') {
if (!hasSpecial) {
hasSpecial = true;
if (sp >= sbuf.length) {
int newCapcity = sbuf.length * 2;
if (sp > newCapcity) {
newCapcity = sp;
}
char[] newsbuf = new char[newCapcity];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
copyTo(np + 1, sp, sbuf);
// text.getChars(np + 1, np + 1 + sp, sbuf, 0);
// System.arraycopy(buf, np + 1, sbuf, 0, sp);
}
ch = next();
switch (ch) {
case '0':
putChar('\0');
break;
case '1':
putChar('\1');
break;
case '2':
putChar('\2');
break;
case '3':
putChar('\3');
break;
case '4':
putChar('\4');
break;
case '5':
putChar('\5');
break;
case '6':
putChar('\6');
break;
case '7':
putChar('\7');
break;
case 'b': // 8
putChar('\b');
break;
case 't': // 9
putChar('\t');
break;
case 'n': // 10
putChar('\n');
break;
case 'v': // 11
putChar('\u000B');
break;
case 'f': // 12
case 'F':
putChar('\f');
break;
case 'r': // 13
putChar('\r');
break;
case '"': // 34
putChar('"');
break;
case '\'': // 39
putChar('\'');
break;
case '/': // 47
putChar('/');
break;
case '\\': // 92
putChar('\\');
break;
case 'x':
char x1 = next();
char x2 = next();
boolean hex1 = (x1 >= '0' && x1 <= '9')
|| (x1 >= 'a' && x1 <= 'f')
|| (x1 >= 'A' && x1 <= 'F');
boolean hex2 = (x2 >= '0' && x2 <= '9')
|| (x2 >= 'a' && x2 <= 'f')
|| (x2 >= 'A' && x2 <= 'F');
if (!hex1 || !hex2) {
throw new JSONException("invalid escape character \\x" + x1 + x2);
}
char x_char = (char) (digits[x1] * 16 + digits[x2]);
putChar(x_char);
break;
case 'u':
char u1 = next();
char u2 = next();
char u3 = next();
char u4 = next();
int val = Integer.parseInt(new String(new char[] { u1, u2, u3, u4 }), 16);
putChar((char) val);
break;
default:
this.ch = ch;
throw new JSONException("unclosed string : " + ch);
}
continue;
}
if (!hasSpecial) {
sp++;
continue;
}
if (sp == sbuf.length) {
putChar(ch);
} else {
sbuf[sp++] = ch;
}
}
token = JSONToken.LITERAL_STRING;
this.ch = next();
}
public Calendar getCalendar() {
return this.calendar;
}
public TimeZone getTimeZone() {
return timeZone;
}
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
}
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public final int intValue() {
if (np == -1) {
np = 0;
}
int result = 0;
boolean negative = false;
int i = np, max = np + sp;
int limit;
int digit;
if (charAt(np) == '-') {
negative = true;
limit = Integer.MIN_VALUE;
i++;
} else {
limit = -Integer.MAX_VALUE;
}
long multmin = INT_MULTMIN_RADIX_TEN;
if (i < max) {
digit = charAt(i++) - '0';
result = -digit;
}
while (i < max) {
// Accumulating negatively avoids surprises near MAX_VALUE
char chLocal = charAt(i++);
if (chLocal == 'L' || chLocal == 'S' || chLocal == 'B') {
break;
}
digit = chLocal - '0';
if (result < multmin) {
throw new NumberFormatException(numberString());
}
result *= 10;
if (result < limit + digit) {
throw new NumberFormatException(numberString());
}
result -= digit;
}
if (negative) {
if (i > np + 1) {
return result;
} else { /* Only got "-" */
throw new NumberFormatException(numberString());
}
} else {
return -result;
}
}
public abstract byte[] bytesValue();
public void close() {
if (sbuf.length <= 1024 * 8) {
SBUF_LOCAL.set(sbuf);
}
this.sbuf = null;
}
public final boolean isRef() {
if (sp != 4) {
return false;
}
return charAt(np + 1) == '$' //
&& charAt(np + 2) == 'r' //
&& charAt(np + 3) == 'e' //
&& charAt(np + 4) == 'f';
}
public String scanTypeName(SymbolTable symbolTable) {
return null;
}
protected final static char[] typeFieldName = ("\"" + JSON.DEFAULT_TYPE_KEY + "\":\"").toCharArray();
public final int scanType(String type) {
matchStat = UNKNOWN;
if (!charArrayCompare(typeFieldName)) {
return NOT_MATCH_NAME;
}
int bpLocal = this.bp + typeFieldName.length;
final int typeLength = type.length();
for (int i = 0; i < typeLength; ++i) {
if (type.charAt(i) != charAt(bpLocal + i)) {
return NOT_MATCH;
}
}
bpLocal += typeLength;
if (charAt(bpLocal) != '"') {
return NOT_MATCH;
}
this.ch = charAt(++bpLocal);
if (ch == ',') {
this.ch = charAt(++bpLocal);
this.bp = bpLocal;
token = JSONToken.COMMA;
return VALUE;
} else if (ch == '}') {
ch = charAt(++bpLocal);
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bpLocal);
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bpLocal);
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bpLocal);
} else if (ch == EOI) {
token = JSONToken.EOF;
} else {
return NOT_MATCH;
}
matchStat = END;
}
this.bp = bpLocal;
return matchStat;
}
public final boolean matchField(char[] fieldName) {
for (;;) {
if (!charArrayCompare(fieldName)) {
if (isWhitespace(ch)) {
next();
continue;
}
return false;
} else {
break;
}
}
bp = bp + fieldName.length;
ch = charAt(bp);
if (ch == '{') {
next();
token = JSONToken.LBRACE;
} else if (ch == '[') {
next();
token = JSONToken.LBRACKET;
} else if (ch == 'S' && charAt(bp + 1) == 'e' && charAt(bp + 2) == 't' && charAt(bp + 3) == '[') {
bp += 3;
ch = charAt(bp);
token = JSONToken.SET;
} else {
nextToken();
}
return true;
}
public int matchField(long fieldNameHash) {
throw new UnsupportedOperationException();
}
public boolean seekArrayToItem(int index) {
throw new UnsupportedOperationException();
}
public int seekObjectToField(long fieldNameHash, boolean deepScan) {
throw new UnsupportedOperationException();
}
public int seekObjectToField(long[] fieldNameHash) {
throw new UnsupportedOperationException();
}
public int seekObjectToFieldDeepScan(long fieldNameHash) {
throw new UnsupportedOperationException();
}
public void skipObject() {
throw new UnsupportedOperationException();
}
public void skipObject(boolean valid) {
throw new UnsupportedOperationException();
}
public void skipArray() {
throw new UnsupportedOperationException();
}
public abstract int indexOf(char ch, int startIndex);
public abstract String addSymbol(int offset, int len, int hash, final SymbolTable symbolTable);
public String scanFieldString(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return stringDefaultValue();
}
// int index = bp + fieldName.length;
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
if (chLocal != '"') {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
final String strVal;
{
int startIndex = bp + fieldName.length + 1;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
int startIndex2 = bp + fieldName.length + 1; // must re compute
String stringVal = subString(startIndex2, endIndex - startIndex2);
if (stringVal.indexOf('\\') != -1) {
for (;;) {
int slashCount = 0;
for (int i = endIndex - 1; i >= 0; --i) {
if (charAt(i) == '\\') {
slashCount++;
} else {
break;
}
}
if (slashCount % 2 == 0) {
break;
}
endIndex = indexOf('"', endIndex + 1);
}
int chars_len = endIndex - (bp + fieldName.length + 1);
char[] chars = sub_chars( bp + fieldName.length + 1, chars_len);
stringVal = readString(chars, chars_len);
}
offset += (endIndex - (bp + fieldName.length + 1) + 1);
chLocal = charAt(bp + (offset++));
strVal = stringVal;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return strVal;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
return strVal;
}
public String scanString(char expectNextChar) {
matchStat = UNKNOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
if (chLocal == 'n') {
if (charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
offset += 3;
chLocal = charAt(bp + (offset++));
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == expectNextChar) {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return null;
} else {
matchStat = NOT_MATCH;
return null;
}
}
final String strVal;
for (;;) {
if (chLocal == '"') {
int startIndex = bp + offset;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
String stringVal = subString(bp + offset, endIndex - startIndex);
if (stringVal.indexOf('\\') != -1) {
for (; ; ) {
int slashCount = 0;
for (int i = endIndex - 1; i >= 0; --i) {
if (charAt(i) == '\\') {
slashCount++;
} else {
break;
}
}
if (slashCount % 2 == 0) {
break;
}
endIndex = indexOf('"', endIndex + 1);
}
int chars_len = endIndex - startIndex;
char[] chars = sub_chars(bp + 1, chars_len);
stringVal = readString(chars, chars_len);
}
offset += (endIndex - startIndex + 1);
chLocal = charAt(bp + (offset++));
strVal = stringVal;
break;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + (offset++));
continue;
} else {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
}
for (;;) {
if (chLocal == expectNextChar) {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return strVal;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + (offset++));
continue;
} else {
if (chLocal == ']') {
bp += offset;
this.ch = charAt(bp);
matchStat = NOT_MATCH;
}
return strVal;
}
}
}
public long scanFieldSymbol(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
if (chLocal != '"') {
matchStat = NOT_MATCH;
return 0;
}
long hash = fnv1a_64_magic_hashcode;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal == '\"') {
chLocal = charAt(bp + (offset++));
break;
}
hash ^= chLocal;
hash *= fnv1a_64_magic_prime;
if (chLocal == '\\') {
matchStat = NOT_MATCH;
return 0;
}
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return hash;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return 0;
}
return hash;
}
public long scanEnumSymbol(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
if (chLocal != '"') {
matchStat = NOT_MATCH;
return 0;
}
long hash = fnv1a_64_magic_hashcode;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal == '\"') {
chLocal = charAt(bp + (offset++));
break;
}
hash ^= ((chLocal >= 'A' && chLocal <= 'Z') ? (chLocal + 32) : chLocal);
hash *= fnv1a_64_magic_prime;
if (chLocal == '\\') {
matchStat = NOT_MATCH;
return 0;
}
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return hash;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return 0;
}
return hash;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Enum> scanEnum(Class> enumClass, final SymbolTable symbolTable, char serperator) {
String name = scanSymbolWithSeperator(symbolTable, serperator);
if (name == null) {
return null;
}
return Enum.valueOf((Class extends Enum>) enumClass, name);
}
public String scanSymbolWithSeperator(final SymbolTable symbolTable, char serperator) {
matchStat = UNKNOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
if (chLocal == 'n') {
if (charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
offset += 3;
chLocal = charAt(bp + (offset++));
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == serperator) {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return null;
} else {
matchStat = NOT_MATCH;
return null;
}
}
if (chLocal != '"') {
matchStat = NOT_MATCH;
return null;
}
String strVal;
// int start = index;
int hash = 0;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal == '\"') {
// bp = index;
// this.ch = chLocal = charAt(bp);
int start = bp + 0 + 1;
int len = bp + offset - start - 1;
strVal = addSymbol(start, len, hash, symbolTable);
chLocal = charAt(bp + (offset++));
break;
}
hash = 31 * hash + chLocal;
if (chLocal == '\\') {
matchStat = NOT_MATCH;
return null;
}
}
for (;;) {
if (chLocal == serperator) {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return strVal;
} else {
if (isWhitespace(chLocal)) {
chLocal = charAt(bp + (offset++));
continue;
}
matchStat = NOT_MATCH;
return strVal;
}
}
}
public Collection newCollectionByType(Class> type){
if (type.isAssignableFrom(HashSet.class)) {
return new HashSet();
} else if (type.isAssignableFrom(ArrayList.class)) {
return new ArrayList();
} else if (type.isAssignableFrom(LinkedList.class)) {
return new LinkedList();
} else {
try {
return (Collection) type.newInstance();
} catch (Exception e) {
throw new JSONException(e.getMessage(), e);
}
}
}
@SuppressWarnings("unchecked")
public Collection scanFieldStringArray(char[] fieldName, Class> type) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
Collection list = newCollectionByType(type);
// if (type.isAssignableFrom(HashSet.class)) {
// list = new HashSet();
// } else if (type.isAssignableFrom(ArrayList.class)) {
// list = new ArrayList();
// } else {
// try {
// list = (Collection) type.newInstance();
// } catch (Exception e) {
// throw new JSONException(e.getMessage(), e);
// }
// }
// int index = bp + fieldName.length;
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
if (chLocal != '[') {
matchStat = NOT_MATCH;
return null;
}
chLocal = charAt(bp + (offset++));
for (;;) {
// int start = index;
if (chLocal == '"') {
int startIndex = bp + offset;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
int startIndex2 = bp + offset; // must re compute
String stringVal = subString(startIndex2, endIndex - startIndex2);
if (stringVal.indexOf('\\') != -1) {
for (;;) {
int slashCount = 0;
for (int i = endIndex - 1; i >= 0; --i) {
if (charAt(i) == '\\') {
slashCount++;
} else {
break;
}
}
if (slashCount % 2 == 0) {
break;
}
endIndex = indexOf('"', endIndex + 1);
}
int chars_len = endIndex - (bp + offset);
char[] chars = sub_chars(bp + offset, chars_len);
stringVal = readString(chars, chars_len);
}
offset += (endIndex - (bp + offset) + 1);
chLocal = charAt(bp + (offset++));
list.add(stringVal);
} else if (chLocal == 'n' //
&& charAt(bp + offset) == 'u' //
&& charAt(bp + offset + 1) == 'l' //
&& charAt(bp + offset + 2) == 'l') {
offset += 3;
chLocal = charAt(bp + (offset++));
list.add(null);
} else if (chLocal == ']' && list.size() == 0) {
chLocal = charAt(bp + (offset++));
break;
} else {
throw new JSONException("illega str");
}
if (chLocal == ',') {
chLocal = charAt(bp + (offset++));
continue;
}
if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
break;
}
matchStat = NOT_MATCH;
return null;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return list;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
bp += (offset - 1);
token = JSONToken.EOF;
this.ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return list;
}
public void scanStringArray(Collection list, char seperator) {
matchStat = UNKNOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
if (chLocal == 'n'
&& charAt(bp + offset) == 'u'
&& charAt(bp + offset + 1) == 'l'
&& charAt(bp + offset + 2) == 'l'
&& charAt(bp + offset + 3) == seperator
) {
bp += 5;
ch = charAt(bp);
matchStat = VALUE_NULL;
return;
}
if (chLocal != '[') {
matchStat = NOT_MATCH;
return;
}
chLocal = charAt(bp + (offset++));
for (;;) {
if (chLocal == 'n' //
&& charAt(bp + offset) == 'u' //
&& charAt(bp + offset + 1) == 'l' //
&& charAt(bp + offset + 2) == 'l') {
offset += 3;
chLocal = charAt(bp + (offset++));
list.add(null);
} else if (chLocal == ']' && list.size() == 0) {
chLocal = charAt(bp + (offset++));
break;
} else if (chLocal != '"') {
matchStat = NOT_MATCH;
return;
} else {
int startIndex = bp + offset;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
String stringVal = subString(bp + offset, endIndex - startIndex);
if (stringVal.indexOf('\\') != -1) {
for (;;) {
int slashCount = 0;
for (int i = endIndex - 1; i >= 0; --i) {
if (charAt(i) == '\\') {
slashCount++;
} else {
break;
}
}
if (slashCount % 2 == 0) {
break;
}
endIndex = indexOf('"', endIndex + 1);
}
int chars_len = endIndex - startIndex;
char[] chars = sub_chars(bp + offset, chars_len);
stringVal = readString(chars, chars_len);
}
offset += (endIndex - (bp + offset) + 1);
chLocal = charAt(bp + (offset++));
list.add(stringVal);
}
if (chLocal == ',') {
chLocal = charAt(bp + (offset++));
continue;
}
if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
break;
}
matchStat = NOT_MATCH;
return;
}
if (chLocal == seperator) {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return;
} else {
matchStat = NOT_MATCH;
return;
}
}
public int scanFieldInt(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
final boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
int value;
if (chLocal >= '0' && chLocal <= '9') {
value = chLocal - '0';
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + (chLocal - '0');
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
if (value < 0 //
|| offset > 11 + 3 + fieldName.length) {
if (value != Integer.MIN_VALUE //
|| offset != 17 //
|| !negative) {
matchStat = NOT_MATCH;
return 0;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return 0;
}
return negative ? -value : value;
}
public final int[] scanFieldIntArray(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
if (chLocal != '[') {
matchStat = NOT_MATCH_NAME;
return null;
}
chLocal = charAt(bp + (offset++));
int[] array = new int[16];
int arrayIndex = 0;
if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
} else {
for (;;) {
boolean nagative = false;
if (chLocal == '-') {
chLocal = charAt(bp + (offset++));
nagative = true;
}
if (chLocal >= '0' && chLocal <= '9') {
int value = chLocal - '0';
for (; ; ) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + (chLocal - '0');
} else {
break;
}
}
if (arrayIndex >= array.length) {
int[] tmp = new int[array.length * 3 / 2];
System.arraycopy(array, 0, tmp, 0, arrayIndex);
array = tmp;
}
array[arrayIndex++] = nagative ? -value : value;
if (chLocal == ',') {
chLocal = charAt(bp + (offset++));
} else if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
break;
}
} else {
matchStat = NOT_MATCH;
return null;
}
}
}
if (arrayIndex != array.length) {
int[] tmp = new int[arrayIndex];
System.arraycopy(array, 0, tmp, 0, arrayIndex);
array = tmp;
}
if (chLocal == ',') {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return array;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += (offset - 1);
this.next();
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += (offset - 1);
this.next();
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += (offset - 1);
this.next();
} else if (chLocal == EOI) {
bp += (offset - 1);
token = JSONToken.EOF;
ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return array;
}
public boolean scanBoolean(char expectNext) {
matchStat = UNKNOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
boolean value = false;
if (chLocal == 't') {
if (charAt(bp + offset) == 'r' //
&& charAt(bp + offset + 1) == 'u' //
&& charAt(bp + offset + 2) == 'e') {
offset += 3;
chLocal = charAt(bp + (offset++));
value = true;
} else {
matchStat = NOT_MATCH;
return false;
}
} else if (chLocal == 'f') {
if (charAt(bp + offset) == 'a' //
&& charAt(bp + offset + 1) == 'l' //
&& charAt(bp + offset + 2) == 's' //
&& charAt(bp + offset + 3) == 'e') {
offset += 4;
chLocal = charAt(bp + (offset++));
value = false;
} else {
matchStat = NOT_MATCH;
return false;
}
} else if (chLocal == '1') {
chLocal = charAt(bp + (offset++));
value = true;
} else if (chLocal == '0') {
chLocal = charAt(bp + (offset++));
value = false;
}
for (;;) {
if (chLocal == expectNext) {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return value;
} else {
if (isWhitespace(chLocal)) {
chLocal = charAt(bp + (offset++));
continue;
}
matchStat = NOT_MATCH;
return value;
}
}
}
public int scanInt(char expectNext) {
matchStat = UNKNOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(bp + (offset++));
}
final boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
int value;
if (chLocal >= '0' && chLocal <= '9') {
value = chLocal - '0';
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + (chLocal - '0');
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
if (value < 0) {
matchStat = NOT_MATCH;
return 0;
}
} else if (chLocal == 'n' && charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = 0;
offset += 3;
chLocal = charAt(bp + offset++);
if (quote && chLocal == '"') {
chLocal = charAt(bp + offset++);
}
for (;;) {
if (chLocal == ',') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == ']') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACKET;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return 0;
} else {
matchStat = NOT_MATCH;
return 0;
}
for (;;) {
if (chLocal == expectNext) {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
} else {
if (isWhitespace(chLocal)) {
chLocal = charAt(bp + (offset++));
continue;
}
matchStat = NOT_MATCH;
return negative ? -value : value;
}
}
}
public boolean scanFieldBoolean(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return false;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
boolean value;
if (chLocal == 't') {
if (charAt(bp + (offset++)) != 'r') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(bp + (offset++)) != 'u') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(bp + (offset++)) != 'e') {
matchStat = NOT_MATCH;
return false;
}
value = true;
} else if (chLocal == 'f') {
if (charAt(bp + (offset++)) != 'a') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(bp + (offset++)) != 'l') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(bp + (offset++)) != 's') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(bp + (offset++)) != 'e') {
matchStat = NOT_MATCH;
return false;
}
value = false;
} else {
matchStat = NOT_MATCH;
return false;
}
chLocal = charAt(bp + offset++);
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return false;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return false;
}
return value;
}
public long scanFieldLong(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
boolean negative = false;
if (chLocal == '-') {
chLocal = charAt(bp + (offset++));
negative = true;
}
long value;
if (chLocal >= '0' && chLocal <= '9') {
value = chLocal - '0';
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + (chLocal - '0');
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
boolean valid = offset - fieldName.length < 21
&& (value >= 0 || (value == -9223372036854775808L && negative));
if (!valid) {
matchStat = NOT_MATCH;
return 0;
}
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return 0;
}
return negative ? -value : value;
}
public long scanLong(char expectNextChar) {
matchStat = UNKNOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(bp + (offset++));
}
final boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
long value;
if (chLocal >= '0' && chLocal <= '9') {
value = chLocal - '0';
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + (chLocal - '0');
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
boolean valid = value >= 0 || (value == -9223372036854775808L && negative);
if (!valid) {
String val = subString(bp, offset - 1);
throw new NumberFormatException(val);
}
} else if (chLocal == 'n' && charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = 0;
offset += 3;
chLocal = charAt(bp + offset++);
if (quote && chLocal == '"') {
chLocal = charAt(bp + offset++);
}
for (;;) {
if (chLocal == ',') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == ']') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACKET;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return 0;
} else {
matchStat = NOT_MATCH;
return 0;
}
if (quote) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return 0;
} else {
chLocal = charAt(bp + (offset++));
}
}
for (;;) {
if (chLocal == expectNextChar) {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
} else {
if (isWhitespace(chLocal)) {
chLocal = charAt(bp + (offset++));
continue;
}
matchStat = NOT_MATCH;
return value;
}
}
}
public final float scanFieldFloat(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(bp + (offset++));
}
boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
float value;
if (chLocal >= '0' && chLocal <= '9') {
long intVal = chLocal - '0';
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
continue;
} else {
break;
}
}
long power = 1;
boolean small = (chLocal == '.');
if (small) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power = 10;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power *= 10;
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
}
boolean exp = chLocal == 'e' || chLocal == 'E';
if (exp) {
chLocal = charAt(bp + (offset++));
if (chLocal == '+' || chLocal == '-') {
chLocal = charAt(bp + (offset++));
}
for (;;) {
if (chLocal >= '0' && chLocal <= '9') {
chLocal = charAt(bp + (offset++));
} else {
break;
}
}
}
int start, count;
if (quote) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return 0;
} else {
chLocal = charAt(bp + (offset++));
}
start = bp + fieldName.length + 1;
count = bp + offset - start - 2;
} else {
start = bp + fieldName.length;
count = bp + offset - start - 1;
}
if ((!exp) && count < 17) {
value = (float) (((double) intVal) / power);
if (negative) {
value = -value;
}
} else {
String text = this.subString(start, count);
value = Float.parseFloat(text);
}
} else if (chLocal == 'n' && charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = 0;
offset += 3;
chLocal = charAt(bp + offset++);
if (quote && chLocal == '"') {
chLocal = charAt(bp + offset++);
}
for (;;) {
if (chLocal == ',') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == '}') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACE;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return 0;
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
bp += (offset - 1);
token = JSONToken.EOF;
ch = EOI;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return 0;
}
return value;
}
public final float scanFloat(char seperator) {
matchStat = UNKNOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(bp + (offset++));
}
boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
float value;
if (chLocal >= '0' && chLocal <= '9') {
long intVal = chLocal - '0';
for (; ; ) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
continue;
} else {
break;
}
}
long power = 1;
boolean small = (chLocal == '.');
if (small) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power = 10;
for (; ; ) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power *= 10;
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
}
boolean exp = chLocal == 'e' || chLocal == 'E';
if (exp) {
chLocal = charAt(bp + (offset++));
if (chLocal == '+' || chLocal == '-') {
chLocal = charAt(bp + (offset++));
}
for (; ; ) {
if (chLocal >= '0' && chLocal <= '9') {
chLocal = charAt(bp + (offset++));
} else {
break;
}
}
}
// int start, count;
// if (quote) {
// if (chLocal != '"') {
// matchStat = NOT_MATCH;
// return 0;
// } else {
// chLocal = charAt(bp + (offset++));
// }
// start = bp + 1;
// count = bp + offset - start - 2;
// } else {
// start = bp;
// count = bp + offset - start - 1;
// }
// String text = this.subString(start, count);
// value = Float.parseFloat(text);
int start, count;
if (quote) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return 0;
} else {
chLocal = charAt(bp + (offset++));
}
start = bp + 1;
count = bp + offset - start - 2;
} else {
start = bp;
count = bp + offset - start - 1;
}
if ((!exp) && count < 17) {
value = (float) (((double) intVal) / power);
if (negative) {
value = -value;
}
} else {
String text = this.subString(start, count);
value = Float.parseFloat(text);
}
} else if (chLocal == 'n' && charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = 0;
offset += 3;
chLocal = charAt(bp + offset++);
if (quote && chLocal == '"') {
chLocal = charAt(bp + offset++);
}
for (;;) {
if (chLocal == ',') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == ']') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACKET;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return 0;
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == seperator) {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
} else {
matchStat = NOT_MATCH;
return value;
}
}
public double scanDouble(char seperator) {
matchStat = UNKNOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(bp + (offset++));
}
boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
double value;
if (chLocal >= '0' && chLocal <= '9') {
long intVal = chLocal - '0';
for (; ; ) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
continue;
} else {
break;
}
}
long power = 1;
boolean small = (chLocal == '.');
if (small) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power = 10;
for (; ; ) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power *= 10;
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
}
boolean exp = chLocal == 'e' || chLocal == 'E';
if (exp) {
chLocal = charAt(bp + (offset++));
if (chLocal == '+' || chLocal == '-') {
chLocal = charAt(bp + (offset++));
}
for (; ; ) {
if (chLocal >= '0' && chLocal <= '9') {
chLocal = charAt(bp + (offset++));
} else {
break;
}
}
}
int start, count;
if (quote) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return 0;
} else {
chLocal = charAt(bp + (offset++));
}
start = bp + 1;
count = bp + offset - start - 2;
} else {
start = bp;
count = bp + offset - start - 1;
}
if (!exp && count < 17) {
value = ((double) intVal) / power;
if (negative) {
value = -value;
}
} else {
String text = this.subString(start, count);
value = Double.parseDouble(text);
}
} else if (chLocal == 'n' && charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = 0;
offset += 3;
chLocal = charAt(bp + offset++);
if (quote && chLocal == '"') {
chLocal = charAt(bp + offset++);
}
for (;;) {
if (chLocal == ',') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == ']') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACKET;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return 0;
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == seperator) {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
} else {
matchStat = NOT_MATCH;
return value;
}
}
public BigDecimal scanDecimal(char seperator) {
matchStat = UNKNOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(bp + (offset++));
}
boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
BigDecimal value;
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
boolean small = (chLocal == '.');
if (small) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return null;
}
}
boolean exp = chLocal == 'e' || chLocal == 'E';
if (exp) {
chLocal = charAt(bp + (offset++));
if (chLocal == '+' || chLocal == '-') {
chLocal = charAt(bp + (offset++));
}
for (;;) {
if (chLocal >= '0' && chLocal <= '9') {
chLocal = charAt(bp + (offset++));
} else {
break;
}
}
}
int start, count;
if (quote) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return null;
} else {
chLocal = charAt(bp + (offset++));
}
start = bp + 1;
count = bp + offset - start - 2;
} else {
start = bp;
count = bp + offset - start - 1;
}
if (count > 65535) {
throw new JSONException("decimal overflow");
}
char[] chars = this.sub_chars(start, count);
value = new BigDecimal(chars, 0, chars.length, MathContext.UNLIMITED);
} else if (chLocal == 'n' && charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = null;
offset += 3;
chLocal = charAt(bp + offset++);
if (quote && chLocal == '"') {
chLocal = charAt(bp + offset++);
}
for (;;) {
if (chLocal == ',') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == '}') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACE;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return null;
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
}
if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return value;
}
public final float[] scanFieldFloatArray(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
if (chLocal != '[') {
matchStat = NOT_MATCH_NAME;
return null;
}
chLocal = charAt(bp + (offset++));
float[] array = new float[16];
int arrayIndex = 0;
for (;;) {
int start = bp + offset - 1;
boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
if (chLocal >= '0' && chLocal <= '9') {
int intVal = chLocal - '0';
for (; ; ) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
continue;
} else {
break;
}
}
int power = 1;
boolean small = (chLocal == '.');
if (small) {
chLocal = charAt(bp + (offset++));
power = 10;
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
for (; ; ) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power *= 10;
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return null;
}
}
boolean exp = chLocal == 'e' || chLocal == 'E';
if (exp) {
chLocal = charAt(bp + (offset++));
if (chLocal == '+' || chLocal == '-') {
chLocal = charAt(bp + (offset++));
}
for (;;) {
if (chLocal >= '0' && chLocal <= '9') {
chLocal = charAt(bp + (offset++));
} else {
break;
}
}
}
int count = bp + offset - start - 1;
float value;
if (!exp && count < 10) {
value = ((float) intVal) / power;
if (negative) {
value = -value;
}
} else {
String text = this.subString(start, count);
value = Float.parseFloat(text);
}
if (arrayIndex >= array.length) {
float[] tmp = new float[array.length * 3 / 2];
System.arraycopy(array, 0, tmp, 0, arrayIndex);
array = tmp;
}
array[arrayIndex++] = value;
if (chLocal == ',') {
chLocal = charAt(bp + (offset++));
} else if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
break;
}
} else {
matchStat = NOT_MATCH;
return null;
}
}
if (arrayIndex != array.length) {
float[] tmp = new float[arrayIndex];
System.arraycopy(array, 0, tmp, 0, arrayIndex);
array = tmp;
}
if (chLocal == ',') {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return array;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += (offset - 1);
this.next();
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += (offset - 1);
this.next();
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += (offset - 1);
this.next();
} else if (chLocal == EOI) {
bp += (offset - 1);
token = JSONToken.EOF;
ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return array;
}
public final float[][] scanFieldFloatArray2(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
if (chLocal != '[') {
matchStat = NOT_MATCH_NAME;
return null;
}
chLocal = charAt(bp + (offset++));
float[][] arrayarray = new float[16][];
int arrayarrayIndex = 0;
for (;;) {
if (chLocal == '[') {
chLocal = charAt(bp + (offset++));
float[] array = new float[16];
int arrayIndex = 0;
for (; ; ) {
int start = bp + offset - 1;
boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
if (chLocal >= '0' && chLocal <= '9') {
int intVal = chLocal - '0';
for (; ; ) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
continue;
} else {
break;
}
}
int power = 1;
if (chLocal == '.') {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power = 10;
for (; ; ) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power *= 10;
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return null;
}
}
boolean exp = chLocal == 'e' || chLocal == 'E';
if (exp) {
chLocal = charAt(bp + (offset++));
if (chLocal == '+' || chLocal == '-') {
chLocal = charAt(bp + (offset++));
}
for (;;) {
if (chLocal >= '0' && chLocal <= '9') {
chLocal = charAt(bp + (offset++));
} else {
break;
}
}
}
int count = bp + offset - start - 1;
float value;
if (!exp && count < 10) {
value = ((float) intVal) / power;
if (negative) {
value = -value;
}
} else {
String text = this.subString(start, count);
value = Float.parseFloat(text);
}
if (arrayIndex >= array.length) {
float[] tmp = new float[array.length * 3 / 2];
System.arraycopy(array, 0, tmp, 0, arrayIndex);
array = tmp;
}
array[arrayIndex++] = value;
if (chLocal == ',') {
chLocal = charAt(bp + (offset++));
} else if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
break;
}
} else {
matchStat = NOT_MATCH;
return null;
}
}
// compact
if (arrayIndex != array.length) {
float[] tmp = new float[arrayIndex];
System.arraycopy(array, 0, tmp, 0, arrayIndex);
array = tmp;
}
if (arrayarrayIndex >= arrayarray.length) {
float[][] tmp = new float[arrayarray.length * 3 / 2][];
System.arraycopy(array, 0, tmp, 0, arrayIndex);
arrayarray = tmp;
}
arrayarray[arrayarrayIndex++] = array;
if (chLocal == ',') {
chLocal = charAt(bp + (offset++));
} else if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
break;
}
} else {
break;
}
}
// compact
if (arrayarrayIndex != arrayarray.length) {
float[][] tmp = new float[arrayarrayIndex][];
System.arraycopy(arrayarray, 0, tmp, 0, arrayarrayIndex);
arrayarray = tmp;
}
if (chLocal == ',') {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return arrayarray;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += (offset - 1);
this.next();
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += (offset - 1);
this.next();
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += (offset - 1);
this.next();
} else if (chLocal == EOI) {
bp += (offset - 1);
token = JSONToken.EOF;
ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return arrayarray;
}
public final double scanFieldDouble(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(bp + (offset++));
}
boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
double value;
if (chLocal >= '0' && chLocal <= '9') {
long intVal = chLocal - '0';
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
continue;
} else {
break;
}
}
long power = 1;
boolean small = (chLocal == '.');
if (small) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power = 10;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power *= 10;
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
}
boolean exp = chLocal == 'e' || chLocal == 'E';
if (exp) {
chLocal = charAt(bp + (offset++));
if (chLocal == '+' || chLocal == '-') {
chLocal = charAt(bp + (offset++));
}
for (;;) {
if (chLocal >= '0' && chLocal <= '9') {
chLocal = charAt(bp + (offset++));
} else {
break;
}
}
}
int start, count;
if (quote) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return 0;
} else {
chLocal = charAt(bp + (offset++));
}
start = bp + fieldName.length + 1;
count = bp + offset - start - 2;
} else {
start = bp + fieldName.length;
count = bp + offset - start - 1;
}
if (!exp && count < 17) {
value = ((double) intVal) / power;
if (negative) {
value = -value;
}
} else {
String text = this.subString(start, count);
value = Double.parseDouble(text);
}
} else if (chLocal == 'n' &&
charAt(bp + offset) == 'u' &&
charAt(bp + offset + 1) == 'l' &&
charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = 0;
offset += 3;
chLocal = charAt(bp + offset++);
if (quote && chLocal == '"') {
chLocal = charAt(bp + offset++);
}
for (;;) {
if (chLocal == ',') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == '}') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACE;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return 0;
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return 0;
}
return value;
}
public BigDecimal scanFieldDecimal(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(bp + (offset++));
}
boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
BigDecimal value;
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
boolean small = (chLocal == '.');
if (small) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return null;
}
}
boolean exp = chLocal == 'e' || chLocal == 'E';
if (exp) {
chLocal = charAt(bp + (offset++));
if (chLocal == '+' || chLocal == '-') {
chLocal = charAt(bp + (offset++));
}
for (;;) {
if (chLocal >= '0' && chLocal <= '9') {
chLocal = charAt(bp + (offset++));
} else {
break;
}
}
}
int start, count;
if (quote) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return null;
} else {
chLocal = charAt(bp + (offset++));
}
start = bp + fieldName.length + 1;
count = bp + offset - start - 2;
} else {
start = bp + fieldName.length;
count = bp + offset - start - 1;
}
if (count > 65535) {
throw new JSONException("scan decimal overflow");
}
char[] chars = this.sub_chars(start, count);
value = new BigDecimal(chars, 0, chars.length, MathContext.UNLIMITED);
} else if (chLocal == 'n' &&
charAt(bp + offset) == 'u' &&
charAt(bp + offset + 1) == 'l' &&
charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = null;
offset += 3;
chLocal = charAt(bp + offset++);
if (quote && chLocal == '"') {
chLocal = charAt(bp + offset++);
}
for (;;) {
if (chLocal == ',') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == '}') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACE;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return null;
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return value;
}
public BigInteger scanFieldBigInteger(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(bp + (offset++));
}
boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
BigInteger value;
if (chLocal >= '0' && chLocal <= '9') {
long intVal = chLocal - '0';
boolean overflow = false;
long temp;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
temp = intVal * 10 + (chLocal - '0');
if (temp < intVal) {
overflow = true;
break;
}
intVal = temp;
continue;
} else {
break;
}
}
int start, count;
if (quote) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return null;
} else {
chLocal = charAt(bp + (offset++));
}
start = bp + fieldName.length + 1;
count = bp + offset - start - 2;
} else {
start = bp + fieldName.length;
count = bp + offset - start - 1;
}
if (!overflow && (count < 20 || (negative && count < 21))) {
value = BigInteger.valueOf(negative ? -intVal : intVal);
} else {
// char[] chars = this.sub_chars(negative ? start + 1 : start, count);
// value = new BigInteger(chars, )
if (count > 65535) {
throw new JSONException("scanInteger overflow");
}
String strVal = this.subString(start, count);
value = new BigInteger(strVal, 10);
}
} else if (chLocal == 'n' &&
charAt(bp + offset) == 'u' &&
charAt(bp + offset + 1) == 'l' &&
charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
value = null;
offset += 3;
chLocal = charAt(bp + offset++);
if (quote && chLocal == '"') {
chLocal = charAt(bp + offset++);
}
for (;;) {
if (chLocal == ',') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == '}') {
bp += offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACE;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(bp + offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return null;
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return value;
}
public java.util.Date scanFieldDate(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
// int index = bp + fieldName.length;
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
final java.util.Date dateVal;
if (chLocal == '"'){
int startIndex = bp + fieldName.length + 1;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
int startIndex2 = bp + fieldName.length + 1; // must re compute
String stringVal = subString(startIndex2, endIndex - startIndex2);
if (stringVal.indexOf('\\') != -1) {
for (;;) {
int slashCount = 0;
for (int i = endIndex - 1; i >= 0; --i) {
if (charAt(i) == '\\') {
slashCount++;
} else {
break;
}
}
if (slashCount % 2 == 0) {
break;
}
endIndex = indexOf('"', endIndex + 1);
}
int chars_len = endIndex - (bp + fieldName.length + 1);
char[] chars = sub_chars( bp + fieldName.length + 1, chars_len);
stringVal = readString(chars, chars_len);
}
offset += (endIndex - (bp + fieldName.length + 1) + 1);
chLocal = charAt(bp + (offset++));
JSONScanner dateLexer = new JSONScanner(stringVal);
try {
if (dateLexer.scanISO8601DateIfMatch(false)) {
Calendar calendar = dateLexer.getCalendar();
dateVal = calendar.getTime();
} else {
matchStat = NOT_MATCH;
return null;
}
} finally {
dateLexer.close();
}
} else if (chLocal == '-' || (chLocal >= '0' && chLocal <= '9')) {
long millis = 0;
boolean negative = false;
if (chLocal == '-') {
chLocal = charAt(bp + (offset++));
negative = true;
}
if (chLocal >= '0' && chLocal <= '9') {
millis = chLocal - '0';
for (; ; ) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
millis = millis * 10 + (chLocal - '0');
} else {
break;
}
}
}
if (millis < 0) {
matchStat = NOT_MATCH;
return null;
}
if (negative) {
millis = -millis;
}
dateVal = new java.util.Date(millis);
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return dateVal;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return dateVal;
}
public java.util.Date scanDate(char seperator) {
matchStat = UNKNOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
final java.util.Date dateVal;
if (chLocal == '"'){
int startIndex = bp + 1;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
int startIndex2 = bp + 1; // must re compute
String stringVal = subString(startIndex2, endIndex - startIndex2);
if (stringVal.indexOf('\\') != -1) {
for (;;) {
int slashCount = 0;
for (int i = endIndex - 1; i >= 0; --i) {
if (charAt(i) == '\\') {
slashCount++;
} else {
break;
}
}
if (slashCount % 2 == 0) {
break;
}
endIndex = indexOf('"', endIndex + 1);
}
int chars_len = endIndex - (bp + 1);
char[] chars = sub_chars( bp + 1, chars_len);
stringVal = readString(chars, chars_len);
}
offset += (endIndex - (bp + 1) + 1);
chLocal = charAt(bp + (offset++));
JSONScanner dateLexer = new JSONScanner(stringVal);
try {
if (dateLexer.scanISO8601DateIfMatch(false)) {
Calendar calendar = dateLexer.getCalendar();
dateVal = calendar.getTime();
} else {
matchStat = NOT_MATCH;
return null;
}
} finally {
dateLexer.close();
}
} else if (chLocal == '-' || (chLocal >= '0' && chLocal <= '9')) {
long millis = 0;
boolean negative = false;
if (chLocal == '-') {
chLocal = charAt(bp + (offset++));
negative = true;
}
if (chLocal >= '0' && chLocal <= '9') {
millis = chLocal - '0';
for (; ; ) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
millis = millis * 10 + (chLocal - '0');
} else {
break;
}
}
}
if (millis < 0) {
matchStat = NOT_MATCH;
return null;
}
if (negative) {
millis = -millis;
}
dateVal = new java.util.Date(millis);
} else if (chLocal == 'n' &&
charAt(bp + offset) == 'u' &&
charAt(bp + offset + 1) == 'l' &&
charAt(bp + offset + 2) == 'l') {
matchStat = VALUE_NULL;
dateVal = null;
offset += 3;
chLocal = charAt(bp + offset++);
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return dateVal;
}
if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return dateVal;
}
public java.util.UUID scanFieldUUID(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
// int index = bp + fieldName.length;
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
final java.util.UUID uuid;
if (chLocal == '"') {
int startIndex = bp + fieldName.length + 1;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
int startIndex2 = bp + fieldName.length + 1; // must re compute
int len = endIndex - startIndex2;
if (len == 36) {
long mostSigBits = 0, leastSigBits = 0;
for (int i = 0; i < 8; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
mostSigBits <<= 4;
mostSigBits |= num;
}
for (int i = 9; i < 13; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
mostSigBits <<= 4;
mostSigBits |= num;
}
for (int i = 14; i < 18; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
mostSigBits <<= 4;
mostSigBits |= num;
}
for (int i = 19; i < 23; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
leastSigBits <<= 4;
leastSigBits |= num;
}
for (int i = 24; i < 36; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
leastSigBits <<= 4;
leastSigBits |= num;
}
uuid = new UUID(mostSigBits, leastSigBits);
offset += (endIndex - (bp + fieldName.length + 1) + 1);
chLocal = charAt(bp + (offset++));
} else if (len == 32) {
long mostSigBits = 0, leastSigBits = 0;
for (int i = 0; i < 16; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
mostSigBits <<= 4;
mostSigBits |= num;
}
for (int i = 16; i < 32; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
leastSigBits <<= 4;
leastSigBits |= num;
}
uuid = new UUID(mostSigBits, leastSigBits);
offset += (endIndex - (bp + fieldName.length + 1) + 1);
chLocal = charAt(bp + (offset++));
} else {
matchStat = NOT_MATCH;
return null;
}
} else if (chLocal == 'n'
&& charAt(bp + (offset++)) == 'u'
&& charAt(bp + (offset++)) == 'l'
&& charAt(bp + (offset++)) == 'l') {
uuid = null;
chLocal = charAt(bp + (offset++));
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return uuid;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return uuid;
}
public java.util.UUID scanUUID(char seperator) {
matchStat = UNKNOWN;
// int index = bp + fieldName.length;
int offset = 0;
char chLocal = charAt(bp + (offset++));
final java.util.UUID uuid;
if (chLocal == '"') {
int startIndex = bp + 1;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
int startIndex2 = bp + 1; // must re compute
int len = endIndex - startIndex2;
if (len == 36) {
long mostSigBits = 0, leastSigBits = 0;
for (int i = 0; i < 8; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
mostSigBits <<= 4;
mostSigBits |= num;
}
for (int i = 9; i < 13; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
mostSigBits <<= 4;
mostSigBits |= num;
}
for (int i = 14; i < 18; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
mostSigBits <<= 4;
mostSigBits |= num;
}
for (int i = 19; i < 23; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
leastSigBits <<= 4;
leastSigBits |= num;
}
for (int i = 24; i < 36; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
leastSigBits <<= 4;
leastSigBits |= num;
}
uuid = new UUID(mostSigBits, leastSigBits);
offset += (endIndex - (bp + 1) + 1);
chLocal = charAt(bp + (offset++));
} else if (len == 32) {
long mostSigBits = 0, leastSigBits = 0;
for (int i = 0; i < 16; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
mostSigBits <<= 4;
mostSigBits |= num;
}
for (int i = 16; i < 32; ++i) {
char ch = charAt(startIndex2 + i);
int num;
if (ch >= '0' && ch <= '9') {
num = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
num = 10 + (ch - 'a');
} else if (ch >= 'A' && ch <= 'F') {
num = 10 + (ch - 'A');
} else {
matchStat = NOT_MATCH_NAME;
return null;
}
leastSigBits <<= 4;
leastSigBits |= num;
}
uuid = new UUID(mostSigBits, leastSigBits);
offset += (endIndex - (bp + 1) + 1);
chLocal = charAt(bp + (offset++));
} else {
matchStat = NOT_MATCH;
return null;
}
} else if (chLocal == 'n'
&& charAt(bp + (offset++)) == 'u'
&& charAt(bp + (offset++)) == 'l'
&& charAt(bp + (offset++)) == 'l') {
uuid = null;
chLocal = charAt(bp + (offset++));
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
return uuid;
}
if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return uuid;
}
public final void scanTrue() {
if (ch != 't') {
throw new JSONException("error parse true");
}
next();
if (ch != 'r') {
throw new JSONException("error parse true");
}
next();
if (ch != 'u') {
throw new JSONException("error parse true");
}
next();
if (ch != 'e') {
throw new JSONException("error parse true");
}
next();
if (ch == ' ' ||
ch == ',' ||
ch == '}' ||
ch == ']' ||
ch == '\n' ||
ch == '\r' ||
ch == '\t' ||
ch == EOI ||
ch == '\f' ||
ch == '\b' ||
ch == ':' ||
ch == '/') {
token = JSONToken.TRUE;
} else {
throw new JSONException("scan true error");
}
}
public final void scanNullOrNew() {
scanNullOrNew(true);
}
public final void scanNullOrNew(boolean acceptColon) {
if (ch != 'n') {
throw new JSONException("error parse null or new");
}
next();
if (ch == 'u') {
next();
if (ch != 'l') {
throw new JSONException("error parse null");
}
next();
if (ch != 'l') {
throw new JSONException("error parse null");
}
next();
if (ch == ' '
|| ch == ','
|| ch == '}'
|| ch == ']'
|| ch == '\n'
|| ch == '\r'
|| ch == '\t'
|| ch == EOI
|| (ch == ':' && acceptColon)
|| ch == '\f'
|| ch == '\b') {
token = JSONToken.NULL;
} else {
throw new JSONException("scan null error");
}
return;
}
if (ch != 'e') {
throw new JSONException("error parse new");
}
next();
if (ch != 'w') {
throw new JSONException("error parse new");
}
next();
if (ch == ' ' ||
ch == ',' ||
ch == '}' ||
ch == ']' ||
ch == '\n' ||
ch == '\r' ||
ch == '\t' ||
ch == EOI ||
ch == '\f' ||
ch == '\b') {
token = JSONToken.NEW;
} else {
throw new JSONException("scan new error");
}
}
public final void scanFalse() {
if (ch != 'f') {
throw new JSONException("error parse false");
}
next();
if (ch != 'a') {
throw new JSONException("error parse false");
}
next();
if (ch != 'l') {
throw new JSONException("error parse false");
}
next();
if (ch != 's') {
throw new JSONException("error parse false");
}
next();
if (ch != 'e') {
throw new JSONException("error parse false");
}
next();
if (ch == ' ' ||
ch == ',' ||
ch == '}' ||
ch == ']' ||
ch == '\n' ||
ch == '\r' ||
ch == '\t' ||
ch == EOI ||
ch == '\f' ||
ch == '\b' ||
ch == ':' ||
ch == '/') {
token = JSONToken.FALSE;
} else {
throw new JSONException("scan false error");
}
}
public final void scanIdent() {
np = bp - 1;
hasSpecial = false;
for (;;) {
sp++;
next();
if (Character.isLetterOrDigit(ch)) {
continue;
}
String ident = stringVal();
if ("null".equalsIgnoreCase(ident)) {
token = JSONToken.NULL;
} else if ("new".equals(ident)) {
token = JSONToken.NEW;
} else if ("true".equals(ident)) {
token = JSONToken.TRUE;
} else if ("false".equals(ident)) {
token = JSONToken.FALSE;
} else if ("undefined".equals(ident)) {
token = JSONToken.UNDEFINED;
} else if ("Set".equals(ident)) {
token = JSONToken.SET;
} else if ("TreeSet".equals(ident)) {
token = JSONToken.TREE_SET;
} else {
token = JSONToken.IDENTIFIER;
}
return;
}
}
public abstract String stringVal();
public abstract String subString(int offset, int count);
protected abstract char[] sub_chars(int offset, int count);
public static String readString(char[] chars, int chars_len) {
char[] sbuf = new char[chars_len];
int len = 0;
for (int i = 0; i < chars_len; ++i) {
char ch = chars[i];
if (ch != '\\') {
sbuf[len++] = ch;
continue;
}
ch = chars[++i];
switch (ch) {
case '0':
sbuf[len++] = '\0';
break;
case '1':
sbuf[len++] = '\1';
break;
case '2':
sbuf[len++] = '\2';
break;
case '3':
sbuf[len++] = '\3';
break;
case '4':
sbuf[len++] = '\4';
break;
case '5':
sbuf[len++] = '\5';
break;
case '6':
sbuf[len++] = '\6';
break;
case '7':
sbuf[len++] = '\7';
break;
case 'b': // 8
sbuf[len++] = '\b';
break;
case 't': // 9
sbuf[len++] = '\t';
break;
case 'n': // 10
sbuf[len++] = '\n';
break;
case 'v': // 11
sbuf[len++] = '\u000B';
break;
case 'f': // 12
case 'F':
sbuf[len++] = '\f';
break;
case 'r': // 13
sbuf[len++] = '\r';
break;
case '"': // 34
sbuf[len++] = '"';
break;
case '\'': // 39
sbuf[len++] = '\'';
break;
case '/': // 47
sbuf[len++] = '/';
break;
case '\\': // 92
sbuf[len++] = '\\';
break;
case 'x':
sbuf[len++] = (char) (digits[chars[++i]] * 16 + digits[chars[++i]]);
break;
case 'u':
sbuf[len++] = (char) Integer.parseInt(new String(new char[] { chars[++i], //
chars[++i], //
chars[++i], //
chars[++i] }),
16);
break;
default:
throw new JSONException("unclosed.str.lit");
}
}
return new String(sbuf, 0, len);
}
protected abstract boolean charArrayCompare(char[] chars);
public boolean isBlankInput() {
for (int i = 0;; ++i) {
char chLocal = charAt(i);
if (chLocal == EOI) {
token = JSONToken.EOF;
break;
}
if (!isWhitespace(chLocal)) {
return false;
}
}
return true;
}
public final void skipWhitespace() {
for (;;) {
if (ch <= '/') {
if (ch == ' ' ||
ch == '\r' ||
ch == '\n' ||
ch == '\t' ||
ch == '\f' ||
ch == '\b') {
next();
continue;
} else if (ch == '/') {
skipComment();
continue;
} else {
break;
}
} else {
break;
}
}
}
private void scanStringSingleQuote() {
np = bp;
hasSpecial = false;
char chLocal;
for (;;) {
chLocal = next();
if (chLocal == '\'') {
break;
}
if (chLocal == EOI) {
if (!isEOF()) {
putChar((char) EOI);
continue;
}
throw new JSONException("unclosed single-quote string");
}
if (chLocal == '\\') {
if (!hasSpecial) {
hasSpecial = true;
if (sp > sbuf.length) {
char[] newsbuf = new char[sp * 2];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
// text.getChars(offset, offset + count, dest, 0);
this.copyTo(np + 1, sp, sbuf);
// System.arraycopy(buf, np + 1, sbuf, 0, sp);
}
chLocal = next();
switch (chLocal) {
case '0':
putChar('\0');
break;
case '1':
putChar('\1');
break;
case '2':
putChar('\2');
break;
case '3':
putChar('\3');
break;
case '4':
putChar('\4');
break;
case '5':
putChar('\5');
break;
case '6':
putChar('\6');
break;
case '7':
putChar('\7');
break;
case 'b': // 8
putChar('\b');
break;
case 't': // 9
putChar('\t');
break;
case 'n': // 10
putChar('\n');
break;
case 'v': // 11
putChar('\u000B');
break;
case 'f': // 12
case 'F':
putChar('\f');
break;
case 'r': // 13
putChar('\r');
break;
case '"': // 34
putChar('"');
break;
case '\'': // 39
putChar('\'');
break;
case '/': // 47
putChar('/');
break;
case '\\': // 92
putChar('\\');
break;
case 'x':
char x1 = next();
char x2 = next();
boolean hex1 = (x1 >= '0' && x1 <= '9')
|| (x1 >= 'a' && x1 <= 'f')
|| (x1 >= 'A' && x1 <= 'F');
boolean hex2 = (x2 >= '0' && x2 <= '9')
|| (x2 >= 'a' && x2 <= 'f')
|| (x2 >= 'A' && x2 <= 'F');
if (!hex1 || !hex2) {
throw new JSONException("invalid escape character \\x" + x1 + x2);
}
putChar((char) (digits[x1] * 16 + digits[x2]));
break;
case 'u':
putChar((char) Integer.parseInt(new String(new char[] { next(), next(), next(), next() }), 16));
break;
default:
this.ch = chLocal;
throw new JSONException("unclosed single-quote string");
}
continue;
}
if (!hasSpecial) {
sp++;
continue;
}
if (sp == sbuf.length) {
putChar(chLocal);
} else {
sbuf[sp++] = chLocal;
}
}
token = LITERAL_STRING;
this.next();
}
/**
* Append a character to sbuf.
*/
protected final void putChar(char ch) {
if (sp >= sbuf.length) {
int len = sbuf.length * 2;
if (len < sp) {
len = sp + 1;
}
char[] newsbuf = new char[len];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
sbuf[sp++] = ch;
}
public final void scanHex() {
if (ch != 'x') {
throw new JSONException("illegal state. " + ch);
}
next();
if (ch != '\'') {
throw new JSONException("illegal state. " + ch);
}
np = bp;
next();
if (ch == '\'') {
next();
token = JSONToken.HEX;
return;
}
for (int i = 0;;++i) {
char ch = next();
if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) {
sp++;
continue;
} else if (ch == '\'') {
sp++;
next();
break;
} else {
throw new JSONException("illegal state. " + ch);
}
}
token = JSONToken.HEX;
}
public final void scanNumber() {
np = bp;
if (ch == '-') {
sp++;
next();
}
for (;;) {
if (ch >= '0' && ch <= '9') {
sp++;
} else {
break;
}
next();
}
boolean isDouble = false;
if (ch == '.') {
sp++;
next();
isDouble = true;
for (;;) {
if (ch >= '0' && ch <= '9') {
sp++;
} else {
break;
}
next();
}
}
if (sp > 65535) {
throw new JSONException("scanNumber overflow");
}
if (ch == 'L') {
sp++;
next();
} else if (ch == 'S') {
sp++;
next();
} else if (ch == 'B') {
sp++;
next();
} else if (ch == 'F') {
sp++;
next();
isDouble = true;
} else if (ch == 'D') {
sp++;
next();
isDouble = true;
} else if (ch == 'e' || ch == 'E') {
sp++;
next();
if (ch == '+' || ch == '-') {
sp++;
next();
}
for (;;) {
if (ch >= '0' && ch <= '9') {
sp++;
} else {
break;
}
next();
}
if (ch == 'D' || ch == 'F') {
sp++;
next();
}
isDouble = true;
}
if (isDouble) {
token = JSONToken.LITERAL_FLOAT;
} else {
token = JSONToken.LITERAL_INT;
}
}
public final long longValue() throws NumberFormatException {
long result = 0;
boolean negative = false;
long limit;
int digit;
if (np == -1) {
np = 0;
}
int i = np, max = np + sp;
if (charAt(np) == '-') {
negative = true;
limit = Long.MIN_VALUE;
i++;
} else {
limit = -Long.MAX_VALUE;
}
long multmin = MULTMIN_RADIX_TEN;
if (i < max) {
digit = charAt(i++) - '0';
result = -digit;
}
while (i < max) {
// Accumulating negatively avoids surprises near MAX_VALUE
char chLocal = charAt(i++);
if (chLocal == 'L' || chLocal == 'S' || chLocal == 'B') {
break;
}
digit = chLocal - '0';
if (result < multmin) {
throw new NumberFormatException(numberString());
}
result *= 10;
if (result < limit + digit) {
throw new NumberFormatException(numberString());
}
result -= digit;
}
if (negative) {
if (i > np + 1) {
return result;
} else { /* Only got "-" */
throw new NumberFormatException(numberString());
}
} else {
return -result;
}
}
public final Number decimalValue(boolean decimal) {
char chLocal = charAt(np + sp - 1);
try {
if (chLocal == 'F') {
return Float.parseFloat(numberString());
}
if (chLocal == 'D') {
return Double.parseDouble(numberString());
}
if (decimal) {
return decimalValue();
} else {
return doubleValue();
}
} catch (NumberFormatException ex) {
throw new JSONException(ex.getMessage() + ", " + info());
}
}
public abstract BigDecimal decimalValue();
public static boolean isWhitespace(char ch) {
// 专门调整了判断顺序
return ch <= ' ' &&
(ch == ' ' ||
ch == '\n' ||
ch == '\r' ||
ch == '\t' ||
ch == '\f' ||
ch == '\b');
}
protected static final long MULTMIN_RADIX_TEN = Long.MIN_VALUE / 10;
protected static final int INT_MULTMIN_RADIX_TEN = Integer.MIN_VALUE / 10;
protected final static int[] digits = new int[(int) 'f' + 1];
static {
for (int i = '0'; i <= '9'; ++i) {
digits[i] = i - '0';
}
for (int i = 'a'; i <= 'f'; ++i) {
digits[i] = (i - 'a') + 10;
}
for (int i = 'A'; i <= 'F'; ++i) {
digits[i] = (i - 'A') + 10;
}
}
/**
* hsf support
* @param fieldName
* @param argTypesCount
* @param typeSymbolTable
* @return
*/
public String[] scanFieldStringArray(char[] fieldName, int argTypesCount, SymbolTable typeSymbolTable) {
throw new UnsupportedOperationException();
}
public boolean matchField2(char[] fieldName) {
throw new UnsupportedOperationException();
}
public int getFeatures() {
return this.features;
}
public void setFeatures(int features) {
this.features = features;
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/JSONReaderScanner.java
================================================
/*
* Copyright 1999-2017 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.fastjson.parser;
import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.math.MathContext;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.util.IOUtils;
//这个类,为了性能优化做了很多特别处理,一切都是为了性能!!!
/**
* @author wenshao[szujobs@hotmail.com]
*/
public final class JSONReaderScanner extends JSONLexerBase {
private final static ThreadLocal BUF_LOCAL = new ThreadLocal();
private Reader reader;
private char[] buf;
private int bufLength;
public JSONReaderScanner(String input){
this(input, JSON.DEFAULT_PARSER_FEATURE);
}
public JSONReaderScanner(String input, int features){
this(new StringReader(input), features);
}
public JSONReaderScanner(char[] input, int inputLength){
this(input, inputLength, JSON.DEFAULT_PARSER_FEATURE);
}
public JSONReaderScanner(Reader reader){
this(reader, JSON.DEFAULT_PARSER_FEATURE);
}
public JSONReaderScanner(Reader reader, int features){
super(features);
this.reader = reader;
buf = BUF_LOCAL.get();
if (buf != null) {
BUF_LOCAL.set(null);
}
if (buf == null) {
buf = new char[1024 * 16];
}
try {
bufLength = reader.read(buf);
} catch (IOException e) {
throw new JSONException(e.getMessage(), e);
}
bp = -1;
next();
if (ch == 65279) { // utf8 bom
next();
}
}
public JSONReaderScanner(char[] input, int inputLength, int features){
this(new CharArrayReader(input, 0, inputLength), features);
}
public final char charAt(int index) {
if (index >= bufLength) {
if (bufLength == -1) {
if (index < sp) {
return buf[index];
}
return EOI;
}
if (bp == 0) {
char[] buf = new char[(this.buf.length * 3) / 2];
System.arraycopy(this.buf, bp, buf, 0, bufLength);
int rest = buf.length - bufLength;
try {
int len = reader.read(buf, bufLength, rest);
bufLength += len;
this.buf = buf;
} catch (IOException e) {
throw new JSONException(e.getMessage(), e);
}
} else {
int rest = bufLength - bp;
if (rest > 0) {
System.arraycopy(buf, bp, buf, 0, rest);
}
try {
bufLength = reader.read(buf, rest, buf.length - rest);
} catch (IOException e) {
throw new JSONException(e.getMessage(), e);
}
if (bufLength == 0) {
throw new JSONException("illegal state, textLength is zero");
}
if (bufLength == -1) {
return EOI;
}
bufLength += rest;
index -= bp;
np -= bp;
bp = 0;
}
}
return buf[index];
}
public final int indexOf(char ch, int startIndex) {
int offset = startIndex - bp;
for (;; ++offset) {
final int index = bp + offset;
char chLoal = charAt(index);
if (ch == chLoal) {
return offset + bp;
}
if (chLoal == EOI) {
return -1;
}
}
}
public final String addSymbol(int offset, int len, int hash, final SymbolTable symbolTable) {
return symbolTable.addSymbol(buf, offset, len, hash);
}
public final char next() {
int index = ++bp;
if (index >= bufLength) {
if (bufLength == -1) {
return EOI;
}
if (sp > 0) {
int offset;
offset = bufLength - sp;
if (ch == '"' && offset > 0) {
offset--;
}
System.arraycopy(buf, offset, buf, 0, sp);
}
np = -1;
index = bp = sp;
try {
int startPos = bp;
int readLength = buf.length - startPos;
if (readLength == 0) {
char[] newBuf = new char[buf.length * 2];
System.arraycopy(buf, 0, newBuf, 0, buf.length);
buf = newBuf;
readLength = buf.length - startPos;
}
bufLength = reader.read(buf, bp, readLength);
} catch (IOException e) {
throw new JSONException(e.getMessage(), e);
}
if (bufLength == 0) {
throw new JSONException("illegal stat, textLength is zero");
}
if (bufLength == -1) {
return ch = EOI;
}
bufLength += bp;
}
return ch = buf[index];
}
protected final void copyTo(int offset, int count, char[] dest) {
System.arraycopy(buf, offset, dest, 0, count);
}
public final boolean charArrayCompare(char[] chars) {
for (int i = 0; i < chars.length; ++i) {
if (charAt(bp + i) != chars[i]) {
return false;
}
}
return true;
}
public byte[] bytesValue() {
if (token == JSONToken.HEX) {
throw new JSONException("TODO");
}
return IOUtils.decodeBase64(buf, np + 1, sp);
}
protected final void arrayCopy(int srcPos, char[] dest, int destPos, int length) {
System.arraycopy(buf, srcPos, dest, destPos, length);
}
/**
* The value of a literal token, recorded as a string. For integers, leading 0x and 'l' suffixes are suppressed.
*/
public final String stringVal() {
if (!hasSpecial) {
int offset = np + 1;
if (offset < 0) {
throw new IllegalStateException();
}
if (offset > buf.length - sp) {
throw new IllegalStateException();
}
return new String(buf, offset, sp);
// return text.substring(np + 1, np + 1 + sp);
} else {
return new String(sbuf, 0, sp);
}
}
public final String subString(int offset, int count) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
return new String(buf, offset, count);
// return text.substring(offset, offset + count);
}
public final char[] sub_chars(int offset, int count) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset == 0) {
return buf;
}
char[] chars = new char[count];
System.arraycopy(buf, offset, chars, 0, count);
return chars;
}
public final String numberString() {
int offset = np;
if (offset == -1) {
offset = 0;
}
char chLocal = charAt(offset + sp - 1);
int sp = this.sp;
if (chLocal == 'L' || chLocal == 'S' || chLocal == 'B' || chLocal == 'F' || chLocal == 'D') {
sp--;
}
String value = new String(buf, offset, sp);
return value;
}
public final BigDecimal decimalValue() {
int offset = np;
if (offset == -1) {
offset = 0;
}
char chLocal = charAt(offset + sp - 1);
int sp = this.sp;
if (chLocal == 'L' || chLocal == 'S' || chLocal == 'B' || chLocal == 'F' || chLocal == 'D') {
sp--;
}
if (sp > 65535) {
throw new JSONException("decimal overflow");
}
return new BigDecimal(buf, offset, sp, MathContext.UNLIMITED);
}
public void close() {
super.close();
if (buf.length <= 1024 * 64) {
BUF_LOCAL.set(buf);
}
this.buf = null;
IOUtils.close(reader);
}
@Override
public boolean isEOF() {
return bufLength == -1 || bp == buf.length || ch == EOI && bp + 1 >= buf.length;
}
public final boolean isBlankInput() {
for (int i = 0;; ++i) {
char chLocal = buf[i];
if (chLocal == EOI) {
token = JSONToken.EOF;
break;
}
if (!isWhitespace(chLocal)) {
return false;
}
}
return true;
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/JSONScanner.java
================================================
/*
* Copyright 1999-2017 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.fastjson.parser;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.util.ASMUtils;
import com.alibaba.fastjson.util.IOUtils;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.*;
import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_hashcode;
import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_prime;
//这个类,为了性能优化做了很多特别处理,一切都是为了性能!!!
/**
* @author wenshao[szujobs@hotmail.com]
*/
public final class JSONScanner extends JSONLexerBase {
private final String text;
private final int len;
public JSONScanner(String input){
this(input, JSON.DEFAULT_PARSER_FEATURE);
}
public JSONScanner(String input, int features){
super(features);
text = input;
len = text.length();
bp = -1;
next();
if (ch == 65279) { // utf-8 bom
next();
}
}
public final char charAt(int index) {
if (index >= len) {
return EOI;
}
return text.charAt(index);
}
public final char next() {
int index = ++bp;
return ch = (index >= this.len ? //
EOI //
: text.charAt(index));
}
public JSONScanner(char[] input, int inputLength){
this(input, inputLength, JSON.DEFAULT_PARSER_FEATURE);
}
public JSONScanner(char[] input, int inputLength, int features){
this(new String(input, 0, inputLength), features);
}
protected final void copyTo(int offset, int count, char[] dest) {
text.getChars(offset, offset + count, dest, 0);
}
static boolean charArrayCompare(String src, int offset, char[] dest) {
final int destLen = dest.length;
if (destLen + offset > src.length()) {
return false;
}
for (int i = 0; i < destLen; ++i) {
if (dest[i] != src.charAt(offset + i)) {
return false;
}
}
return true;
}
public final boolean charArrayCompare(char[] chars) {
return charArrayCompare(text, bp, chars);
}
public final int indexOf(char ch, int startIndex) {
return text.indexOf(ch, startIndex);
}
public final String addSymbol(int offset, int len, int hash, final SymbolTable symbolTable) {
return symbolTable.addSymbol(text, offset, len, hash);
}
public byte[] bytesValue() {
if (token == JSONToken.HEX) {
int start = np + 1, len = sp;
if (len % 2 != 0) {
throw new JSONException("illegal state. " + len);
}
byte[] bytes = new byte[len / 2];
for (int i = 0; i < bytes.length; ++i) {
char c0 = text.charAt(start + i * 2);
char c1 = text.charAt(start + i * 2 + 1);
int b0 = c0 - (c0 <= 57 ? 48 : 55);
int b1 = c1 - (c1 <= 57 ? 48 : 55);
bytes[i] = (byte) ((b0 << 4) | b1);
}
return bytes;
}
if (!hasSpecial) {
return IOUtils.decodeBase64(text, np + 1, sp);
} else {
String escapedText = new String(sbuf, 0, sp);
return IOUtils.decodeBase64(escapedText);
}
}
/**
* The value of a literal token, recorded as a string. For integers, leading 0x and 'l' suffixes are suppressed.
*/
public final String stringVal() {
if (!hasSpecial) {
return this.subString(np + 1, sp);
} else {
return new String(sbuf, 0, sp);
}
}
public final String subString(int offset, int count) {
if (ASMUtils.IS_ANDROID) {
if (count < sbuf.length) {
text.getChars(offset, offset + count, sbuf, 0);
return new String(sbuf, 0, count);
} else {
char[] chars = new char[count];
text.getChars(offset, offset + count, chars, 0);
return new String(chars);
}
} else {
return text.substring(offset, offset + count);
}
}
public final char[] sub_chars(int offset, int count) {
if (ASMUtils.IS_ANDROID && count < sbuf.length) {
text.getChars(offset, offset + count, sbuf, 0);
return sbuf;
} else {
char[] chars = new char[count];
text.getChars(offset, offset + count, chars, 0);
return chars;
}
}
public final String numberString() {
char chLocal = charAt(np + sp - 1);
int sp = this.sp;
if (chLocal == 'L' || chLocal == 'S' || chLocal == 'B' || chLocal == 'F' || chLocal == 'D') {
sp--;
}
return this.subString(np, sp);
}
public final BigDecimal decimalValue() {
char chLocal = charAt(np + sp - 1);
int sp = this.sp;
if (chLocal == 'L' || chLocal == 'S' || chLocal == 'B' || chLocal == 'F' || chLocal == 'D') {
sp--;
}
if (sp > 65535) {
throw new JSONException("decimal overflow");
}
int offset = np, count = sp;
if (count < sbuf.length) {
text.getChars(offset, offset + count, sbuf, 0);
return new BigDecimal(sbuf, 0, count, MathContext.UNLIMITED);
} else {
char[] chars = new char[count];
text.getChars(offset, offset + count, chars, 0);
return new BigDecimal(chars, 0, chars.length, MathContext.UNLIMITED);
}
}
public boolean scanISO8601DateIfMatch() {
return scanISO8601DateIfMatch(true);
}
public boolean scanISO8601DateIfMatch(boolean strict) {
int rest = len - bp;
return scanISO8601DateIfMatch(strict, rest);
}
private boolean scanISO8601DateIfMatch(boolean strict, int rest) {
if (rest < 8) {
return false;
}
char c0 = charAt(bp);
char c1 = charAt(bp + 1);
char c2 = charAt(bp + 2);
char c3 = charAt(bp + 3);
char c4 = charAt(bp + 4);
char c5 = charAt(bp + 5);
char c6 = charAt(bp + 6);
char c7 = charAt(bp + 7);
if ((!strict) && rest > 13) {
char c_r0 = charAt(bp + rest - 1);
char c_r1 = charAt(bp + rest - 2);
if (c0 == '/' && c1 == 'D' && c2 == 'a' && c3 == 't' && c4 == 'e' && c5 == '(' && c_r0 == '/'
&& c_r1 == ')') {
int plusIndex = -1;
for (int i = 6; i < rest; ++i) {
char c = charAt(bp + i);
if (c == '+') {
plusIndex = i;
} else if (c < '0' || c > '9') {
break;
}
}
if (plusIndex == -1) {
return false;
}
int offset = bp + 6;
String numberText = this.subString(offset, bp + plusIndex - offset);
long millis = Long.parseLong(numberText);
calendar = Calendar.getInstance(timeZone, locale);
calendar.setTimeInMillis(millis);
token = JSONToken.LITERAL_ISO8601_DATE;
return true;
}
}
char c10;
if (rest == 8
|| rest == 14
|| (rest == 16 && ((c10 = charAt(bp + 10)) == 'T' || c10 == ' '))
|| (rest == 17 && charAt(bp + 6) != '-')) {
if (strict) {
return false;
}
char y0, y1, y2, y3, M0, M1, d0, d1;
char c8 = charAt(bp + 8);
final boolean c_47 = c4 == '-' && c7 == '-';
final boolean sperate16 = c_47 && rest == 16;
final boolean sperate17 = c_47 && rest == 17;
if (sperate17 || sperate16) {
y0 = c0;
y1 = c1;
y2 = c2;
y3 = c3;
M0 = c5;
M1 = c6;
d0 = c8;
d1 = charAt(bp + 9);
} else if (c4 == '-' && c6 == '-') {
y0 = c0;
y1 = c1;
y2 = c2;
y3 = c3;
M0 = '0';
M1 = c5;
d0 = '0';
d1 = c7;
} else {
y0 = c0;
y1 = c1;
y2 = c2;
y3 = c3;
M0 = c4;
M1 = c5;
d0 = c6;
d1 = c7;
}
if (!checkDate(y0, y1, y2, y3, M0, M1, d0, d1)) {
return false;
}
setCalendar(y0, y1, y2, y3, M0, M1, d0, d1);
int hour, minute, seconds, millis;
if (rest != 8) {
char c9 = charAt(bp + 9);
c10 = charAt(bp + 10);
char c11 = charAt(bp + 11);
char c12 = charAt(bp + 12);
char c13 = charAt(bp + 13);
char h0, h1, m0, m1, s0, s1;
if ((sperate17 && c10 == 'T' && c13 == ':' && charAt(bp + 16) == 'Z')
|| (sperate16 && (c10 == ' ' || c10 == 'T') && c13 == ':')) {
h0 = c11;
h1 = c12;
m0 = charAt(bp + 14);
m1 = charAt(bp + 15);
s0 = '0';
s1 = '0';
} else {
h0 = c8;
h1 = c9;
m0 = c10;
m1 = c11;
s0 = c12;
s1 = c13;
}
if (!checkTime(h0, h1, m0, m1, s0, s1)) {
return false;
}
if (rest == 17 && !sperate17) {
char S0 = charAt(bp + 14);
char S1 = charAt(bp + 15);
char S2 = charAt(bp + 16);
if (S0 < '0' || S0 > '9') {
return false;
}
if (S1 < '0' || S1 > '9') {
return false;
}
if (S2 < '0' || S2 > '9') {
return false;
}
millis = (S0 - '0') * 100 + (S1 - '0') * 10 + (S2 - '0');
} else {
millis = 0;
}
hour = (h0 - '0') * 10 + (h1 - '0');
minute = (m0 - '0') * 10 + (m1 - '0');
seconds = (s0 - '0') * 10 + (s1 - '0');
} else {
hour = 0;
minute = 0;
seconds = 0;
millis = 0;
}
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, seconds);
calendar.set(Calendar.MILLISECOND, millis);
token = JSONToken.LITERAL_ISO8601_DATE;
return true;
}
if (rest < 9) {
return false;
}
char c8 = charAt(bp + 8);
char c9 = charAt(bp + 9);
int date_len = 10;
char y0, y1, y2, y3, M0, M1, d0, d1;
if ((c4 == '-' && c7 == '-') // cn
|| (c4 == '/' && c7 == '/') // tw yyyy/mm/dd
) {
y0 = c0;
y1 = c1;
y2 = c2;
y3 = c3;
M0 = c5;
M1 = c6;
if (c9 == ' ') {
d0 = '0';
d1 = c8;
date_len = 9;
} else {
d0 = c8;
d1 = c9;
}
} else if ((c4 == '-' && c6 == '-') // cn yyyy-m-dd
) {
y0 = c0;
y1 = c1;
y2 = c2;
y3 = c3;
M0 = '0';
M1 = c5;
if (c8 == ' ') {
d0 = '0';
d1 = c7;
date_len = 8;
} else {
d0 = c7;
d1 = c8;
date_len = 9;
}
} else if ((c2 == '.' && c5 == '.') // de dd.mm.yyyy
|| (c2 == '-' && c5 == '-') // in dd-mm-yyyy
) {
d0 = c0;
d1 = c1;
M0 = c3;
M1 = c4;
y0 = c6;
y1 = c7;
y2 = c8;
y3 = c9;
} else if (c8 == 'T') {
y0 = c0;
y1 = c1;
y2 = c2;
y3 = c3;
M0 = c4;
M1 = c5;
d0 = c6;
d1 = c7;
date_len = 8;
} else {
if (c4 == '年' || c4 == '년') {
y0 = c0;
y1 = c1;
y2 = c2;
y3 = c3;
if (c7 == '月' || c7 == '월') {
M0 = c5;
M1 = c6;
if (c9 == '日' || c9 == '일') {
d0 = '0';
d1 = c8;
} else if (charAt(bp + 10) == '日' || charAt(bp + 10) == '일'){
d0 = c8;
d1 = c9;
date_len = 11;
} else {
return false;
}
} else if (c6 == '月' || c6 == '월') {
M0 = '0';
M1 = c5;
if (c8 == '日' || c8 == '일') {
d0 = '0';
d1 = c7;
} else if (c9 == '日' || c9 == '일'){
d0 = c7;
d1 = c8;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
if (!checkDate(y0, y1, y2, y3, M0, M1, d0, d1)) {
return false;
}
setCalendar(y0, y1, y2, y3, M0, M1, d0, d1);
char t = charAt(bp + date_len);
if (t == 'T' && rest == 16 && date_len == 8 && charAt(bp + 15) == 'Z') {
char h0 = charAt(bp + date_len + 1);
char h1 = charAt(bp + date_len + 2);
char m0 = charAt(bp + date_len + 3);
char m1 = charAt(bp + date_len + 4);
char s0 = charAt(bp + date_len + 5);
char s1 = charAt(bp + date_len + 6);
if (!checkTime(h0, h1, m0, m1, s0, s1)) {
return false;
}
setTime(h0, h1, m0, m1, s0, s1);
calendar.set(Calendar.MILLISECOND, 0);
if (calendar.getTimeZone().getRawOffset() != 0) {
String[] timeZoneIDs = TimeZone.getAvailableIDs(0);
if (timeZoneIDs.length > 0) {
TimeZone timeZone = TimeZone.getTimeZone(timeZoneIDs[0]);
calendar.setTimeZone(timeZone);
}
}
token = JSONToken.LITERAL_ISO8601_DATE;
return true;
} else if (t == 'T' || (t == ' ' && !strict)) {
if (rest < date_len + 9) { // "0000-00-00T00:00:00".length()
return false;
}
} else if (t == '"' || t == EOI || t == '日' || t == '일') {
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
ch = charAt(bp += date_len);
token = JSONToken.LITERAL_ISO8601_DATE;
return true;
} else if (t == '+' || t == '-') {
if (len == date_len + 6) {
if (charAt(bp + date_len + 3) != ':' //
|| charAt(bp + date_len + 4) != '0' //
|| charAt(bp + date_len + 5) != '0') {
return false;
}
setTime('0', '0', '0', '0', '0', '0');
calendar.set(Calendar.MILLISECOND, 0);
setTimeZone(t, charAt(bp + date_len + 1), charAt(bp + date_len + 2));
return true;
}
return false;
} else {
return false;
}
if (charAt(bp + date_len + 3) != ':') {
return false;
}
if (charAt(bp + date_len + 6) != ':') {
return false;
}
char h0 = charAt(bp + date_len + 1);
char h1 = charAt(bp + date_len + 2);
char m0 = charAt(bp + date_len + 4);
char m1 = charAt(bp + date_len + 5);
char s0 = charAt(bp + date_len + 7);
char s1 = charAt(bp + date_len + 8);
if (!checkTime(h0, h1, m0, m1, s0, s1)) {
return false;
}
setTime(h0, h1, m0, m1, s0, s1);
char dot = charAt(bp + date_len + 9);
int millisLen = -1; // 有可能没有毫秒区域,没有毫秒区域的时候下一个字符位置有可能是'Z'、'+'、'-'
int millis = 0;
if (dot == '.') { // 0000-00-00T00:00:00.000
if (rest < date_len + 11) {
return false;
}
char S0 = charAt(bp + date_len + 10);
if (S0 < '0' || S0 > '9') {
return false;
}
millis = S0 - '0';
millisLen = 1;
if (rest > date_len + 11) {
char S1 = charAt(bp + date_len + 11);
if (S1 >= '0' && S1 <= '9') {
millis = millis * 10 + (S1 - '0');
millisLen = 2;
}
}
if (millisLen == 2) {
char S2 = charAt(bp + date_len + 12);
if (S2 >= '0' && S2 <= '9') {
millis = millis * 10 + (S2 - '0');
millisLen = 3;
}
}
}
calendar.set(Calendar.MILLISECOND, millis);
int timzeZoneLength = 0;
char timeZoneFlag = charAt(bp + date_len + 10 + millisLen);
if (timeZoneFlag == ' ') {
millisLen++;
timeZoneFlag = charAt(bp + date_len + 10 + millisLen);
}
if (timeZoneFlag == '+' || timeZoneFlag == '-') {
char t0 = charAt(bp + date_len + 10 + millisLen + 1);
if (t0 < '0' || t0 > '1') {
return false;
}
char t1 = charAt(bp + date_len + 10 + millisLen + 2);
if (t1 < '0' || t1 > '9') {
return false;
}
char t2 = charAt(bp + date_len + 10 + millisLen + 3);
char t3 = '0', t4 = '0';
if (t2 == ':') { // ThreeLetterISO8601TimeZone
t3 = charAt(bp + date_len + 10 + millisLen + 4);
t4 = charAt(bp + date_len + 10 + millisLen + 5);
if(t3 == '4' && t4 == '5') {
// handle some special timezones like xx:45
if (t0 == '1' && (t1 == '2' || t1 == '3')) {
// NZ-CHAT => +12:45
// Pacific/Chatham => +12:45
// NZ-CHAT => +13:45 (DST)
// Pacific/Chatham => +13:45 (DST)
} else if (t0 == '0' && (t1 == '5' || t1 == '8')) {
// Asia/Kathmandu => +05:45
// Asia/Katmandu => +05:45
// Australia/Eucla => +08:45
} else {
return false;
}
} else {
//handle normal timezone like xx:00 and xx:30
if (t3 != '0' && t3 != '3') {
return false;
}
if (t4 != '0') {
return false;
}
}
timzeZoneLength = 6;
} else if (t2 == '0') { // TwoLetterISO8601TimeZone
t3 = charAt(bp + date_len + 10 + millisLen + 4);
if (t3 != '0' && t3 != '3') {
return false;
}
timzeZoneLength = 5;
} else if (t2 == '3' && charAt(bp + date_len + 10 + millisLen + 4) == '0') {
t3 = '3';
t4 = '0';
timzeZoneLength = 5;
} else if (t2 == '4' && charAt(bp + date_len + 10 + millisLen + 4) == '5') {
t3 = '4';
t4 = '5';
timzeZoneLength = 5;
} else {
timzeZoneLength = 3;
}
setTimeZone(timeZoneFlag, t0, t1, t3, t4);
} else if (timeZoneFlag == 'Z') {// UTC
timzeZoneLength = 1;
if (calendar.getTimeZone().getRawOffset() != 0) {
String[] timeZoneIDs = TimeZone.getAvailableIDs(0);
if (timeZoneIDs.length > 0) {
TimeZone timeZone = TimeZone.getTimeZone(timeZoneIDs[0]);
calendar.setTimeZone(timeZone);
}
}
}
char end = charAt(bp + (date_len + 10 + millisLen + timzeZoneLength));
if (end != EOI && end != '"') {
return false;
}
ch = charAt(bp += (date_len + 10 + millisLen + timzeZoneLength));
token = JSONToken.LITERAL_ISO8601_DATE;
return true;
}
protected void setTime(char h0, char h1, char m0, char m1, char s0, char s1) {
int hour = (h0 - '0') * 10 + (h1 - '0');
int minute = (m0 - '0') * 10 + (m1 - '0');
int seconds = (s0 - '0') * 10 + (s1 - '0');
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, seconds);
}
protected void setTimeZone(char timeZoneFlag, char t0, char t1) {
setTimeZone(timeZoneFlag, t0, t1, '0', '0');
}
protected void setTimeZone(char timeZoneFlag, char t0, char t1, char t3, char t4) {
int timeZoneOffset = ((t0 - '0') * 10 + (t1 - '0')) * 3600 * 1000;
timeZoneOffset += ((t3 - '0') * 10 + (t4 - '0')) * 60 * 1000;
if (timeZoneFlag == '-') {
timeZoneOffset = -timeZoneOffset;
}
if (calendar.getTimeZone().getRawOffset() != timeZoneOffset) {
calendar.setTimeZone(new SimpleTimeZone(timeZoneOffset, Integer.toString(timeZoneOffset)));
}
}
private boolean checkTime(char h0, char h1, char m0, char m1, char s0, char s1) {
if (h0 == '0') {
if (h1 < '0' || h1 > '9') {
return false;
}
} else if (h0 == '1') {
if (h1 < '0' || h1 > '9') {
return false;
}
} else if (h0 == '2') {
if (h1 < '0' || h1 > '4') {
return false;
}
} else {
return false;
}
if (m0 >= '0' && m0 <= '5') {
if (m1 < '0' || m1 > '9') {
return false;
}
} else if (m0 == '6') {
if (m1 != '0') {
return false;
}
} else {
return false;
}
if (s0 >= '0' && s0 <= '5') {
if (s1 < '0' || s1 > '9') {
return false;
}
} else if (s0 == '6') {
if (s1 != '0') {
return false;
}
} else {
return false;
}
return true;
}
private void setCalendar(char y0, char y1, char y2, char y3, char M0, char M1, char d0, char d1) {
calendar = Calendar.getInstance(timeZone, locale);
int year = (y0 - '0') * 1000 + (y1 - '0') * 100 + (y2 - '0') * 10 + (y3 - '0');
int month = (M0 - '0') * 10 + (M1 - '0') - 1;
int day = (d0 - '0') * 10 + (d1 - '0');
// calendar.set(year, month, day);
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
}
static boolean checkDate(char y0, char y1, char y2, char y3, char M0, char M1, int d0, int d1) {
if (y0 < '0' || y0 > '9') {
return false;
}
if (y1 < '0' || y1 > '9') {
return false;
}
if (y2 < '0' || y2 > '9') {
return false;
}
if (y3 < '0' || y3 > '9') {
return false;
}
if (M0 == '0') {
if (M1 < '1' || M1 > '9') {
return false;
}
} else if (M0 == '1') {
if (M1 != '0' && M1 != '1' && M1 != '2') {
return false;
}
} else {
return false;
}
if (d0 == '0') {
if (d1 < '1' || d1 > '9') {
return false;
}
} else if (d0 == '1' || d0 == '2') {
if (d1 < '0' || d1 > '9') {
return false;
}
} else if (d0 == '3') {
if (d1 != '0' && d1 != '1') {
return false;
}
} else {
return false;
}
return true;
}
@Override
public boolean isEOF() {
return bp == len || (ch == EOI && bp + 1 >= len);
}
public int scanFieldInt(char[] fieldName) {
matchStat = UNKNOWN;
int startPos = this.bp;
char startChar = this.ch;
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int index = bp + fieldName.length;
char ch = charAt(index++);
final boolean quote = ch == '"';
if (quote) {
ch = charAt(index++);
}
final boolean negative = ch == '-';
if (negative) {
ch = charAt(index++);
}
int value;
if (ch >= '0' && ch <= '9') {
value = ch - '0';
for (;;) {
ch = charAt(index++);
if (ch >= '0' && ch <= '9') {
int value_10 = value * 10;
if (value_10 < value) {
matchStat = NOT_MATCH;
return 0;
}
value = value_10 + (ch - '0');
} else if (ch == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
if (value < 0) {
matchStat = NOT_MATCH;
return 0;
}
if (quote) {
if (ch != '"') {
matchStat = NOT_MATCH;
return 0;
} else {
ch = charAt(index++);
}
}
for (;;) {
if (ch == ',' || ch == '}') {
bp = index - 1;
break;
} else if(isWhitespace(ch)) {
ch = charAt(index++);
continue;
} else {
matchStat = NOT_MATCH;
return 0;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
}
if (ch == '}') {
bp = index - 1;
ch = charAt(++bp);
for (; ; ) {
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
break;
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
break;
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
break;
} else if (ch == EOI) {
token = JSONToken.EOF;
break;
} else if (isWhitespace(ch)) {
ch = charAt(++bp);
continue;
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return 0;
}
}
matchStat = END;
}
return negative ? -value : value;
}
public String scanFieldString(char[] fieldName) {
matchStat = UNKNOWN;
int startPos = this.bp;
char startChar = this.ch;
for (;;) {
if (!charArrayCompare(text, bp, fieldName)) {
if (isWhitespace(ch)) {
next();
while (isWhitespace(ch)) {
next();
}
continue;
}
matchStat = NOT_MATCH_NAME;
return stringDefaultValue();
} else {
break;
}
}
int index = bp + fieldName.length;
int spaceCount = 0;
char ch = charAt(index++);
if (ch != '"') {
while (isWhitespace(ch)) {
spaceCount++;
ch = charAt(index++);
}
if (ch != '"') {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
}
final String strVal;
{
int startIndex = index;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
String stringVal = subString(startIndex, endIndex - startIndex);
if (stringVal.indexOf('\\') != -1) {
for (;;) {
int slashCount = 0;
for (int i = endIndex - 1; i >= 0; --i) {
if (charAt(i) == '\\') {
slashCount++;
} else {
break;
}
}
if (slashCount % 2 == 0) {
break;
}
endIndex = indexOf('"', endIndex + 1);
}
int chars_len = endIndex - (bp + fieldName.length + 1 + spaceCount);
char[] chars = sub_chars(bp + fieldName.length + 1 + spaceCount, chars_len);
stringVal = readString(chars, chars_len);
}
if ((this.features & Feature.TrimStringFieldValue.mask) != 0) {
stringVal = stringVal.trim();
}
ch = charAt(endIndex + 1);
for (;;) {
if (ch == ',' || ch == '}') {
bp = endIndex + 1;
this.ch = ch;
strVal = stringVal;
break;
} else if (isWhitespace(ch)) {
endIndex++;
ch = charAt(endIndex + 1);
} else {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
}
}
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
return strVal;
} else {
//condition ch == '}' is always 'true'
ch = charAt(++bp);
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
} else if (ch == EOI) {
token = JSONToken.EOF;
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return stringDefaultValue();
}
matchStat = END;
}
return strVal;
}
public java.util.Date scanFieldDate(char[] fieldName) {
matchStat = UNKNOWN;
int startPos = this.bp;
char startChar = this.ch;
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
int index = bp + fieldName.length;
char ch = charAt(index++);
final java.util.Date dateVal;
if (ch == '"') {
int startIndex = index;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
int rest = endIndex - startIndex;
bp = index;
if (scanISO8601DateIfMatch(false, rest)) {
dateVal = calendar.getTime();
} else {
bp = startPos;
matchStat = NOT_MATCH;
return null;
}
ch = charAt(endIndex + 1);
bp = startPos;
for (; ; ) {
if (ch == ',' || ch == '}') {
bp = endIndex + 1;
this.ch = ch;
break;
} else if (isWhitespace(ch)) {
endIndex++;
ch = charAt(endIndex + 1);
} else {
matchStat = NOT_MATCH;
return null;
}
}
} else if (ch == '-' || (ch >= '0' && ch <= '9')) {
long millis = 0;
boolean negative = false;
if (ch == '-') {
ch = charAt(index++);
negative = true;
}
if (ch >= '0' && ch <= '9') {
millis = ch - '0';
for (; ; ) {
ch = charAt(index++);
if (ch >= '0' && ch <= '9') {
millis = millis * 10 + (ch - '0');
} else {
if (ch == ',' || ch == '}') {
bp = index - 1;
}
break;
}
}
}
if (millis < 0) {
matchStat = NOT_MATCH;
return null;
}
if (negative) {
millis = -millis;
}
dateVal = new java.util.Date(millis);
} else {
matchStat = NOT_MATCH;
return null;
}
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return dateVal;
} else {
//condition ch == '}' is always 'true'
ch = charAt(++bp);
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
} else if (ch == EOI) {
token = JSONToken.EOF;
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
}
return dateVal;
}
public long scanFieldSymbol(char[] fieldName) {
matchStat = UNKNOWN;
for (;;) {
if (!charArrayCompare(text, bp, fieldName)) {
if (isWhitespace(ch)) {
next();
while (isWhitespace(ch)) {
next();
}
continue;
}
matchStat = NOT_MATCH_NAME;
return 0;
} else {
break;
}
}
int index = bp + fieldName.length;
int spaceCount = 0;
char ch = charAt(index++);
if (ch != '"') {
while (isWhitespace(ch)) {
ch = charAt(index++);
spaceCount++;
}
if (ch != '"') {
matchStat = NOT_MATCH;
return 0;
}
}
long hash = fnv1a_64_magic_hashcode;
for (;;) {
ch = charAt(index++);
if (ch == '\"') {
bp = index;
this.ch = ch = charAt(bp);
break;
} else if (index > len) {
matchStat = NOT_MATCH;
return 0;
}
hash ^= ch;
hash *= fnv1a_64_magic_prime;
}
for (;;) {
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
return hash;
} else if (ch == '}') {
next();
skipWhitespace();
ch = getCurrent();
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
} else if (ch == EOI) {
token = JSONToken.EOF;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
break;
} else if (isWhitespace(ch)) {
ch = charAt(++bp);
continue;
} else {
matchStat = NOT_MATCH;
return 0;
}
}
return hash;
}
@SuppressWarnings("unchecked")
public Collection scanFieldStringArray(char[] fieldName, Class> type) {
matchStat = UNKNOWN;
while (ch == '\n' || ch == ' ') {
int index = ++bp;
ch = (index >= this.len ? //
EOI //
: text.charAt(index));
}
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
Collection list = newCollectionByType(type);
// if (type.isAssignableFrom(HashSet.class)) {
// list = new HashSet();
// } else if (type.isAssignableFrom(ArrayList.class)) {
// list = new ArrayList();
// } else {
// try {
// list = (Collection) type.newInstance();
// } catch (Exception e) {
// throw new JSONException(e.getMessage(), e);
// }
// }
int startPos = this.bp;
char startChar = this.ch;
int index = bp + fieldName.length;
char ch = charAt(index++);
if (ch == '[') {
ch = charAt(index++);
for (;;) {
if (ch == '"') {
int startIndex = index;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
String stringVal = subString(startIndex, endIndex - startIndex);
if (stringVal.indexOf('\\') != -1) {
for (;;) {
int slashCount = 0;
for (int i = endIndex - 1; i >= 0; --i) {
if (charAt(i) == '\\') {
slashCount++;
} else {
break;
}
}
if (slashCount % 2 == 0) {
break;
}
endIndex = indexOf('"', endIndex + 1);
}
int chars_len = endIndex - startIndex;
char[] chars = sub_chars(startIndex, chars_len);
stringVal = readString(chars, chars_len);
}
index = endIndex + 1;
ch = charAt(index++);
list.add(stringVal);
} else if (ch == 'n' && text.startsWith("ull", index)) {
index += 3;
ch = charAt(index++);
list.add(null);
} else if (ch == ']' && list.size() == 0) {
ch = charAt(index++);
break;
} else {
matchStat = NOT_MATCH;
return null;
}
if (ch == ',') {
ch = charAt(index++);
continue;
}
if (ch == ']') {
ch = charAt(index++);
while (isWhitespace(ch)) {
ch = charAt(index++);
}
break;
}
matchStat = NOT_MATCH;
return null;
}
} else if (text.startsWith("ull", index)) {
index += 3;
ch = charAt(index++);
list = null;
} else {
matchStat = NOT_MATCH;
return null;
}
bp = index;
if (ch == ',') {
this.ch = charAt(bp);
matchStat = VALUE;
return list;
} else if (ch == '}') {
ch = charAt(bp);
for (;;) {
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
break;
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
break;
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
break;
} else if (ch == EOI) {
token = JSONToken.EOF;
this.ch = ch;
break;
} else {
boolean space = false;
while (isWhitespace(ch)) {
ch = charAt(index++);
bp = index;
space = true;
}
if (space) {
continue;
}
matchStat = NOT_MATCH;
return null;
}
}
matchStat = END;
} else {
this.ch = startChar;
bp = startPos;
matchStat = NOT_MATCH;
return null;
}
return list;
}
public long scanFieldLong(char[] fieldName) {
matchStat = UNKNOWN;
int startPos = this.bp;
char startChar = this.ch;
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int index = bp + fieldName.length;
char ch = charAt(index++);
final boolean quote = ch == '"';
if (quote) {
ch = charAt(index++);
}
boolean negative = false;
if (ch == '-') {
ch = charAt(index++);
negative = true;
}
long value;
if (ch >= '0' && ch <= '9') {
value = ch - '0';
for (;;) {
ch = charAt(index++);
if (ch >= '0' && ch <= '9') {
value = value * 10 + (ch - '0');
} else if (ch == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
if (quote) {
if (ch != '"') {
matchStat = NOT_MATCH;
return 0;
} else {
ch = charAt(index++);
}
}
if (ch == ',' || ch == '}') {
bp = index - 1;
}
break;
}
}
boolean valid = value >= 0 || (value == -9223372036854775808L && negative);
if (!valid) {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return 0;
}
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return 0;
}
for (;;) {
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
} else if (ch == '}') {
ch = charAt(++bp);
for (;;) {
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
break;
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
break;
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
break;
} else if (ch == EOI) {
token = JSONToken.EOF;
break;
} else if (isWhitespace(ch)) {
ch = charAt(++bp);
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return 0;
}
}
matchStat = END;
break;
} else if (isWhitespace(ch)) {
bp = index;
ch = charAt(index++);
continue;
} else {
matchStat = NOT_MATCH;
return 0;
}
}
return negative ? -value : value;
}
public boolean scanFieldBoolean(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return false;
}
int startPos = bp;
int index = bp + fieldName.length;
char ch = charAt(index++);
final boolean quote = ch == '"';
if (quote) {
ch = charAt(index++);
}
boolean value;
if (ch == 't') {
if (charAt(index++) != 'r') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(index++) != 'u') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(index++) != 'e') {
matchStat = NOT_MATCH;
return false;
}
if (quote && charAt(index++) != '"') {
matchStat = NOT_MATCH;
return false;
}
bp = index;
ch = charAt(bp);
value = true;
} else if (ch == 'f') {
if (charAt(index++) != 'a') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(index++) != 'l') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(index++) != 's') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(index++) != 'e') {
matchStat = NOT_MATCH;
return false;
}
if (quote && charAt(index++) != '"') {
matchStat = NOT_MATCH;
return false;
}
bp = index;
ch = charAt(bp);
value = false;
} else if (ch == '1') {
if (quote && charAt(index++) != '"') {
matchStat = NOT_MATCH;
return false;
}
bp = index;
ch = charAt(bp);
value = true;
} else if (ch == '0') {
if (quote && charAt(index++) != '"') {
matchStat = NOT_MATCH;
return false;
}
bp = index;
ch = charAt(bp);
value = false;
} else {
matchStat = NOT_MATCH;
return false;
}
for (;;) {
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
token = JSONToken.COMMA;
break;
} else if (ch == '}') {
ch = charAt(++bp);
for (;;) {
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
} else if (ch == EOI) {
token = JSONToken.EOF;
} else if (isWhitespace(ch)) {
ch = charAt(++bp);
continue;
} else {
matchStat = NOT_MATCH;
return false;
}
break;
}
matchStat = END;
break;
} else if (isWhitespace(ch)) {
ch = charAt(++bp);
} else {
bp = startPos;
ch = charAt(bp);
matchStat = NOT_MATCH;
return false;
}
}
return value;
}
public final int scanInt(char expectNext) {
matchStat = UNKNOWN;
final int mark = bp;
int offset = bp;
char chLocal = charAt(offset++);
while (isWhitespace(chLocal)) {
chLocal = charAt(offset++);
}
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(offset++);
}
final boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(offset++);
}
int value;
if (chLocal >= '0' && chLocal <= '9') {
value = chLocal - '0';
for (;;) {
chLocal = charAt(offset++);
if (chLocal >= '0' && chLocal <= '9') {
int value_10 = value * 10;
if (value_10 < value) {
throw new JSONException("parseInt error : "
+ subString(mark, offset - 1));
}
value = value_10 + (chLocal - '0');
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
if (quote) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return 0;
} else {
chLocal = charAt(offset++);
}
}
break;
}
}
if (value < 0) {
matchStat = NOT_MATCH;
return 0;
}
} else if (chLocal == 'n'
&& charAt(offset++) == 'u'
&& charAt(offset++) == 'l'
&& charAt(offset++) == 'l') {
matchStat = VALUE_NULL;
value = 0;
chLocal = charAt(offset++);
if (quote && chLocal == '"') {
chLocal = charAt(offset++);
}
for (;;) {
if (chLocal == ',') {
bp = offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == ']') {
bp = offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACKET;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return 0;
} else {
matchStat = NOT_MATCH;
return 0;
}
for (;;) {
if (chLocal == expectNext) {
bp = offset;
this.ch = charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
} else {
if (isWhitespace(chLocal)) {
chLocal = charAt(offset++);
continue;
}
matchStat = NOT_MATCH;
return negative ? -value : value;
}
}
}
public double scanDouble(char seperator) {
matchStat = UNKNOWN;
int offset = bp;
char chLocal = charAt(offset++);
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(offset++);
}
boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(offset++);
}
double value;
if (chLocal >= '0' && chLocal <= '9') {
long intVal = chLocal - '0';
for (; ; ) {
chLocal = charAt(offset++);
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
continue;
} else {
break;
}
}
long power = 1;
boolean small = (chLocal == '.');
if (small) {
chLocal = charAt(offset++);
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power = 10;
for (; ; ) {
chLocal = charAt(offset++);
if (chLocal >= '0' && chLocal <= '9') {
intVal = intVal * 10 + (chLocal - '0');
power *= 10;
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
}
boolean exp = chLocal == 'e' || chLocal == 'E';
if (exp) {
chLocal = charAt(offset++);
if (chLocal == '+' || chLocal == '-') {
chLocal = charAt(offset++);
}
for (; ; ) {
if (chLocal >= '0' && chLocal <= '9') {
chLocal = charAt(offset++);
} else {
break;
}
}
}
int start, count;
if (quote) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return 0;
} else {
chLocal = charAt(offset++);
}
start = bp + 1;
count = offset - start - 2;
} else {
start = bp;
count = offset - start - 1;
}
if (!exp && count < 18) {
value = ((double) intVal) / power;
if (negative) {
value = -value;
}
} else {
String text = this.subString(start, count);
value = Double.parseDouble(text);
}
} else if (chLocal == 'n'
&& charAt(offset++) == 'u'
&& charAt(offset++) == 'l'
&& charAt(offset++) == 'l') {
matchStat = VALUE_NULL;
value = 0;
chLocal = charAt(offset++);
if (quote && chLocal == '"') {
chLocal = charAt(offset++);
}
for (;;) {
if (chLocal == ',') {
bp = offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == ']') {
bp = offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACKET;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return 0;
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == seperator) {
bp = offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
} else {
matchStat = NOT_MATCH;
return value;
}
}
public long scanLong(char seperator) {
matchStat = UNKNOWN;
int offset = bp;
char chLocal = charAt(offset++);
final boolean quote = chLocal == '"';
if (quote) {
chLocal = charAt(offset++);
}
final boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(offset++);
}
long value;
if (chLocal >= '0' && chLocal <= '9') {
value = chLocal - '0';
for (;;) {
chLocal = charAt(offset++);
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + (chLocal - '0');
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
if (quote) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return 0;
} else {
chLocal = charAt(offset++);
}
}
break;
}
}
boolean valid = value >= 0 || (value == -9223372036854775808L && negative);
if (!valid) {
matchStat = NOT_MATCH;
return 0;
}
} else if (chLocal == 'n'
&& charAt(offset++) == 'u'
&& charAt(offset++) == 'l'
&& charAt(offset++) == 'l') {
matchStat = VALUE_NULL;
value = 0;
chLocal = charAt(offset++);
if (quote && chLocal == '"') {
chLocal = charAt(offset++);
}
for (;;) {
if (chLocal == ',') {
bp = offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.COMMA;
return value;
} else if (chLocal == ']') {
bp = offset;
this.ch = charAt(bp);
matchStat = VALUE_NULL;
token = JSONToken.RBRACKET;
return value;
} else if (isWhitespace(chLocal)) {
chLocal = charAt(offset++);
continue;
}
break;
}
matchStat = NOT_MATCH;
return 0;
} else {
matchStat = NOT_MATCH;
return 0;
}
for (;;) {
if (chLocal == seperator) {
bp = offset;
this.ch = charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
} else {
if (isWhitespace(chLocal)) {
chLocal = charAt(offset++);
continue;
}
matchStat = NOT_MATCH;
return value;
}
}
}
public java.util.Date scanDate(char seperator) {
matchStat = UNKNOWN;
int startPos = this.bp;
char startChar = this.ch;
int index = bp;
char ch = charAt(index++);
final java.util.Date dateVal;
if (ch == '"') {
int startIndex = index;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
int rest = endIndex - startIndex;
bp = index;
if (scanISO8601DateIfMatch(false, rest)) {
dateVal = calendar.getTime();
} else {
bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return null;
}
ch = charAt(endIndex + 1);
bp = startPos;
for (; ; ) {
if (ch == ',' || ch == ']') {
bp = endIndex + 1;
this.ch = ch;
break;
} else if (isWhitespace(ch)) {
endIndex++;
ch = charAt(endIndex + 1);
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return null;
}
}
} else if (ch == '-' || (ch >= '0' && ch <= '9')) {
long millis = 0;
boolean negative = false;
if (ch == '-') {
ch = charAt(index++);
negative = true;
}
if (ch >= '0' && ch <= '9') {
millis = ch - '0';
for (; ; ) {
ch = charAt(index++);
if (ch >= '0' && ch <= '9') {
millis = millis * 10 + (ch - '0');
} else {
if (ch == ',' || ch == ']') {
bp = index - 1;
}
break;
}
}
}
if (millis < 0) {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return null;
}
if (negative) {
millis = -millis;
}
dateVal = new java.util.Date(millis);
} else if (ch == 'n'
&& charAt(index++) == 'u'
&& charAt(index++) == 'l'
&& charAt(index++) == 'l') {
dateVal = null;
ch = charAt(index);
bp = index;
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return null;
}
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
return dateVal;
} else {
//condition ch == '}' is always 'true'
ch = charAt(++bp);
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
} else if (ch == EOI) {
this.ch = EOI;
token = JSONToken.EOF;
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
}
return dateVal;
}
protected final void arrayCopy(int srcPos, char[] dest, int destPos, int length) {
text.getChars(srcPos, srcPos + length, dest, destPos);
}
public String info() {
StringBuilder buf = new StringBuilder();
// buf.append("pos ").append(bp);
// return "pos " + bp //
// + ", json : " //
// + (text.length() < 65536 //
// ? text //
// : text.substring(0, 65536));
int line = 1;
int column = 1;
for (int i = 0; i < bp; ++i, column++) {
char ch = text.charAt(i);
if (ch == '\n') {
column = 1;
line++;
}
}
buf.append("pos ").append(bp)
.append(", line ").append(line)
.append(", column ").append(column);
if (text.length() < 65535) {
buf.append(text);
} else {
buf.append(text.substring(0, 65535));
}
return buf.toString();
}
// for hsf support
public String[] scanFieldStringArray(char[] fieldName, int argTypesCount, SymbolTable typeSymbolTable) {
int startPos = bp;
char starChar = ch;
while (isWhitespace(ch)) {
next();
}
int offset;
char ch;
if (fieldName != null) {
matchStat = UNKNOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
offset = bp + fieldName.length;
ch = text.charAt(offset++);
while (isWhitespace(ch)) {
ch = text.charAt(offset++);
}
if (ch == ':') {
ch = text.charAt(offset++);
} else {
matchStat = NOT_MATCH;
return null;
}
while (isWhitespace(ch)) {
ch = text.charAt(offset++);
}
} else {
offset = bp + 1;
ch = this.ch;
}
if (ch == '[') {
bp = offset;
this.ch = text.charAt(bp);
} else if (ch == 'n' && text.startsWith("ull", bp + 1)) {
bp += 4;
this.ch = text.charAt(bp);
return null;
} else {
matchStat = NOT_MATCH;
return null;
}
String[] types = argTypesCount >= 0 ? new String[argTypesCount] : new String[4];
int typeIndex = 0;
for (;;) {
while (isWhitespace(this.ch)) {
next();
}
if (this.ch != '\"') {
this.bp = startPos;
this.ch = starChar;
matchStat = NOT_MATCH;
return null;
}
String type = scanSymbol(typeSymbolTable, '"');
if (typeIndex == types.length) {
int newCapacity = types.length + (types.length >> 1) + 1;
String[] array = new String[newCapacity];
System.arraycopy(types, 0, array, 0, types.length);
types = array;
}
types[typeIndex++] = type;
while (isWhitespace(this.ch)) {
next();
}
if (this.ch == ',') {
next();
continue;
}
break;
}
if (types.length != typeIndex) {
String[] array = new String[typeIndex];
System.arraycopy(types, 0, array, 0, typeIndex);
types = array;
}
while (isWhitespace(this.ch)) {
next();
}
if (this.ch == ']') {
next();
} else {
this.bp = startPos;
this.ch = starChar;
matchStat = NOT_MATCH;
return null;
}
return types;
}
public boolean matchField2(char[] fieldName) {
while (isWhitespace(ch)) {
next();
}
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return false;
}
int offset = bp + fieldName.length;
char ch = text.charAt(offset++);
while (isWhitespace(ch)) {
ch = text.charAt(offset++);
}
if (ch == ':') {
this.bp = offset;
this.ch = charAt(bp);
return true;
} else {
matchStat = NOT_MATCH_NAME;
return false;
}
}
public final void skipObject() {
skipObject(false);
}
public final void skipObject(boolean valid) {
boolean quote = false;
int braceCnt = 0;
int i = bp;
for (; i < text.length(); ++i) {
final char ch = text.charAt(i);
if (ch == '\\') {
if (i < len - 1) {
++i;
continue;
} else {
this.ch = ch;
this.bp = i;
throw new JSONException("illegal str, " + info());
}
} else if (ch == '"') {
quote = !quote;
} else if (ch == '{') {
if (quote) {
continue;
}
braceCnt++;
} else if (ch == '}') {
if (quote) {
continue;
} else {
braceCnt--;
}
if (braceCnt == -1) {
this.bp = i + 1;
if (this.bp == text.length()) {
this.ch = EOI;
this.token = JSONToken.EOF;
return;
}
this.ch = text.charAt(this.bp);
if (this.ch == ',') {
token = JSONToken.COMMA;
int index = ++bp;
this.ch = (index >= text.length() //
? EOI //
: text.charAt(index));
return;
} else if (this.ch == '}') {
token = JSONToken.RBRACE;
next();
return;
} else if (this.ch == ']') {
token = JSONToken.RBRACKET;
next();
return;
} else {
nextToken(JSONToken.COMMA);
}
return;
}
}
}
for (int j = 0; j < bp; j++) {
if (j < text.length() && text.charAt(j) == ' ') {
i++;
}
}
if (i == text.length()) {
throw new JSONException("illegal str, " + info());
}
}
public final void skipArray() {
skipArray(false);
}
public final void skipArray(boolean valid) {
boolean quote = false;
int bracketCnt = 0;
int i = bp;
for (; i < text.length(); ++i) {
char ch = text.charAt(i);
if (ch == '\\') {
if (i < len - 1) {
++i;
continue;
} else {
this.ch = ch;
this.bp = i;
throw new JSONException("illegal str, " + info());
}
} else if (ch == '"') {
quote = !quote;
} else if (ch == '[') {
if (quote) {
continue;
}
bracketCnt++;
} else if (ch == '{' && valid) {
{
int index = ++bp;
this.ch = (index >= text.length() //
? EOI //
: text.charAt(index));
}
skipObject(valid);
} else if (ch == ']') {
if (quote) {
continue;
} else {
bracketCnt--;
}
if (bracketCnt == -1) {
this.bp = i + 1;
if (this.bp == text.length()) {
this.ch = EOI;
token = JSONToken.EOF;
return;
}
this.ch = text.charAt(this.bp);
nextToken(JSONToken.COMMA);
return;
}
}
}
if (i == text.length()) {
throw new JSONException("illegal str, " + info());
}
}
public final void skipString() {
if (ch == '"') {
for (int i = bp + 1; i < text.length(); ++i) {
char c = text.charAt(i);
if (c == '\\') {
if (i < len - 1) {
++i;
continue;
}
} else if (c == '"') {
this.ch = text.charAt(bp = i + 1);
return;
}
}
throw new JSONException("unclosed str");
} else {
throw new UnsupportedOperationException();
}
}
public boolean seekArrayToItem(int index) {
if (index < 0) {
throw new IllegalArgumentException("index must > 0, but " + index);
}
if (token == JSONToken.EOF) {
return false;
}
if (token != JSONToken.LBRACKET) {
throw new UnsupportedOperationException();
}
// nextToken();
for (int i = 0; i < index; ++i) {
skipWhitespace();
if (ch == '"' || ch == '\'') {
skipString();
if (ch == ',') {
next();
continue;
} else if (ch == ']') {
next();
nextToken(JSONToken.COMMA);
return false;
} else {
throw new JSONException("illegal json.");
}
} else if (ch == '{') {
next();
token = JSONToken.LBRACE;
skipObject(false);
} else if (ch == '[') {
next();
token = JSONToken.LBRACKET;
skipArray(false);
} else {
boolean match = false;
for (int j = bp + 1; j < text.length(); ++j) {
char c = text.charAt(j);
if (c == ',') {
match = true;
bp = j + 1;
ch = charAt(bp);
break;
} else if (c == ']') {
bp = j + 1;
ch = charAt(bp);
nextToken();
return false;
}
}
if (!match) {
throw new JSONException("illegal json.");
}
continue;
}
if (token == JSONToken.COMMA) {
continue;
} else if (token == JSONToken.RBRACKET) {
return false;
} else {
throw new UnsupportedOperationException();
}
}
nextToken();
return true;
}
public int seekObjectToField(long fieldNameHash, boolean deepScan) {
if (token == JSONToken.EOF) {
return JSONLexer.NOT_MATCH;
}
if (token == JSONToken.RBRACE || token == JSONToken.RBRACKET) {
nextToken();
return JSONLexer.NOT_MATCH;
}
if (token != JSONToken.LBRACE && token != JSONToken.COMMA) {
throw new UnsupportedOperationException(JSONToken.name(token));
}
for (;;) {
if (ch == '}') {
next();
nextToken();
return JSONLexer.NOT_MATCH;
}
if (ch == EOI) {
return JSONLexer.NOT_MATCH;
}
if (ch != '"') {
skipWhitespace();
}
long hash;
if (ch == '"') {
hash = fnv1a_64_magic_hashcode;
for (int i = bp + 1; i < text.length(); ++i) {
char c = text.charAt(i);
if (c == '\\') {
++i;
if (i == text.length()) {
throw new JSONException("unclosed str, " + info());
}
c = text.charAt(i);
}
if (c == '"') {
bp = i + 1;
ch = (bp >= text.length() //
? EOI //
: text.charAt(bp));
break;
}
hash ^= c;
hash *= fnv1a_64_magic_prime;
}
} else {
throw new UnsupportedOperationException();
}
if (hash == fieldNameHash) {
if (ch != ':') {
skipWhitespace();
}
if (ch == ':') {
{
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
}
if (ch == ',') {
{
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
}
token = JSONToken.COMMA;
} else if (ch == ']') {
{
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
}
token = JSONToken.RBRACKET;
} else if (ch == '}') {
{
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
}
token = JSONToken.RBRACE;
} else if (ch >= '0' && ch <= '9') {
sp = 0;
pos = bp;
scanNumber();
} else {
nextToken(JSONToken.LITERAL_INT);
}
}
return VALUE;
}
if (ch != ':') {
skipWhitespace();
}
if (ch == ':') {
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
} else {
throw new JSONException("illegal json, " + info());
}
if (ch != '"'
&& ch != '\''
&& ch != '{'
&& ch != '['
&& ch != '0'
&& ch != '1'
&& ch != '2'
&& ch != '3'
&& ch != '4'
&& ch != '5'
&& ch != '6'
&& ch != '7'
&& ch != '8'
&& ch != '9'
&& ch != '+'
&& ch != '-') {
skipWhitespace();
}
// skip fieldValues
if (ch == '-' || ch == '+' || (ch >= '0' && ch <= '9')) {
next();
while (ch >= '0' && ch <= '9') {
next();
}
// scale
if (ch == '.') {
next();
while (ch >= '0' && ch <= '9') {
next();
}
}
// exp
if (ch == 'E' || ch == 'e') {
next();
if (ch == '-' || ch == '+') {
next();
}
while (ch >= '0' && ch <= '9') {
next();
}
}
if (ch != ',') {
skipWhitespace();
}
if (ch == ',') {
next();
}
} else if (ch == '"') {
skipString();
if (ch != ',' && ch != '}') {
skipWhitespace();
}
if (ch == ',') {
next();
}
} else if (ch == 't') {
next();
if (ch == 'r') {
next();
if (ch == 'u') {
next();
if (ch == 'e') {
next();
}
}
}
if (ch != ',' && ch != '}') {
skipWhitespace();
}
if (ch == ',') {
next();
}
} else if (ch == 'n') {
next();
if (ch == 'u') {
next();
if (ch == 'l') {
next();
if (ch == 'l') {
next();
}
}
}
if (ch != ',' && ch != '}') {
skipWhitespace();
}
if (ch == ',') {
next();
}
} else if (ch == 'f') {
next();
if (ch == 'a') {
next();
if (ch == 'l') {
next();
if (ch == 's') {
next();
if (ch == 'e') {
next();
}
}
}
}
if (ch != ',' && ch != '}') {
skipWhitespace();
}
if (ch == ',') {
next();
}
} else if (ch == '{') {
{
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
}
if (deepScan) {
token = JSONToken.LBRACE;
return OBJECT;
}
skipObject(false);
if (token == JSONToken.RBRACE) {
return JSONLexer.NOT_MATCH;
}
} else if (ch == '[') {
next();
if (deepScan) {
token = JSONToken.LBRACKET;
return ARRAY;
}
skipArray(false);
if (token == JSONToken.RBRACE) {
return JSONLexer.NOT_MATCH;
}
} else {
throw new UnsupportedOperationException();
}
}
}
public int seekObjectToField(long[] fieldNameHash) {
if (token != JSONToken.LBRACE && token != JSONToken.COMMA) {
throw new UnsupportedOperationException();
}
for (;;) {
if (ch == '}') {
next();
nextToken();
this.matchStat = JSONLexer.NOT_MATCH;
return -1;
}
if (ch == EOI) {
this.matchStat = JSONLexer.NOT_MATCH;
return -1;
}
if (ch != '"') {
skipWhitespace();
}
long hash;
if (ch == '"') {
hash = fnv1a_64_magic_hashcode;
for (int i = bp + 1; i < text.length(); ++i) {
char c = text.charAt(i);
if (c == '\\') {
++i;
if (i == text.length()) {
throw new JSONException("unclosed str, " + info());
}
c = text.charAt(i);
}
if (c == '"') {
bp = i + 1;
ch = (bp >= text.length() //
? EOI //
: text.charAt(bp));
break;
}
hash ^= c;
hash *= fnv1a_64_magic_prime;
}
} else {
throw new UnsupportedOperationException();
}
int matchIndex = -1;
for (int i = 0; i < fieldNameHash.length; i++) {
if (hash == fieldNameHash[i]) {
matchIndex = i;
break;
}
}
if (matchIndex != -1) {
if (ch != ':') {
skipWhitespace();
}
if (ch == ':') {
{
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
}
if (ch == ',') {
{
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
}
token = JSONToken.COMMA;
} else if (ch == ']') {
{
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
}
token = JSONToken.RBRACKET;
} else if (ch == '}') {
{
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
}
token = JSONToken.RBRACE;
} else if (ch >= '0' && ch <= '9') {
sp = 0;
pos = bp;
scanNumber();
} else {
nextToken(JSONToken.LITERAL_INT);
}
}
matchStat = VALUE;
return matchIndex;
}
if (ch != ':') {
skipWhitespace();
}
if (ch == ':') {
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
} else {
throw new JSONException("illegal json, " + info());
}
if (ch != '"'
&& ch != '\''
&& ch != '{'
&& ch != '['
&& ch != '0'
&& ch != '1'
&& ch != '2'
&& ch != '3'
&& ch != '4'
&& ch != '5'
&& ch != '6'
&& ch != '7'
&& ch != '8'
&& ch != '9'
&& ch != '+'
&& ch != '-') {
skipWhitespace();
}
// skip fieldValues
if (ch == '-' || ch == '+' || (ch >= '0' && ch <= '9')) {
next();
while (ch >= '0' && ch <= '9') {
next();
}
// scale
if (ch == '.') {
next();
while (ch >= '0' && ch <= '9') {
next();
}
}
// exp
if (ch == 'E' || ch == 'e') {
next();
if (ch == '-' || ch == '+') {
next();
}
while (ch >= '0' && ch <= '9') {
next();
}
}
if (ch != ',') {
skipWhitespace();
}
if (ch == ',') {
next();
}
} else if (ch == '"') {
skipString();
if (ch != ',' && ch != '}') {
skipWhitespace();
}
if (ch == ',') {
next();
}
} else if (ch == '{') {
{
int index = ++bp;
ch = (index >= text.length() //
? EOI //
: text.charAt(index));
}
skipObject(false);
} else if (ch == '[') {
next();
skipArray(false);
} else {
throw new UnsupportedOperationException();
}
}
}
public String scanTypeName(SymbolTable symbolTable) {
if (text.startsWith("\"@type\":\"", bp)) {
int p = text.indexOf('"', bp + 9);
if (p != -1) {
bp += 9;
int h = 0;
for (int i = bp; i < p; i++) {
h = 31 * h + text.charAt(i);
}
String typeName = addSymbol(bp, p - bp, h, symbolTable);
char separator = text.charAt(p + 1);
if (separator != ',' && separator != ']') {
return null;
}
bp = p + 2;
ch = text.charAt(bp);
return typeName;
}
}
return null;
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/JSONToken.java
================================================
/*
* Copyright 1999-2017 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.fastjson.parser;
/**
* @author wenshao[szujobs@hotmail.com]
*/
public class JSONToken {
//
public final static int ERROR = 1;
//
public final static int LITERAL_INT = 2;
//
public final static int LITERAL_FLOAT = 3;
//
public final static int LITERAL_STRING = 4;
//
public final static int LITERAL_ISO8601_DATE = 5;
public final static int TRUE = 6;
//
public final static int FALSE = 7;
//
public final static int NULL = 8;
//
public final static int NEW = 9;
//
public final static int LPAREN = 10; // ("("),
//
public final static int RPAREN = 11; // (")"),
//
public final static int LBRACE = 12; // ("{"),
//
public final static int RBRACE = 13; // ("}"),
//
public final static int LBRACKET = 14; // ("["),
//
public final static int RBRACKET = 15; // ("]"),
//
public final static int COMMA = 16; // (","),
//
public final static int COLON = 17; // (":"),
//
public final static int IDENTIFIER = 18;
//
public final static int FIELD_NAME = 19;
public final static int EOF = 20;
public final static int SET = 21;
public final static int TREE_SET = 22;
public final static int UNDEFINED = 23; // undefined
public final static int SEMI = 24;
public final static int DOT = 25;
public final static int HEX = 26;
public static String name(int value) {
switch (value) {
case ERROR:
return "error";
case LITERAL_INT:
return "int";
case LITERAL_FLOAT:
return "float";
case LITERAL_STRING:
return "string";
case LITERAL_ISO8601_DATE:
return "iso8601";
case TRUE:
return "true";
case FALSE:
return "false";
case NULL:
return "null";
case NEW:
return "new";
case LPAREN:
return "(";
case RPAREN:
return ")";
case LBRACE:
return "{";
case RBRACE:
return "}";
case LBRACKET:
return "[";
case RBRACKET:
return "]";
case COMMA:
return ",";
case COLON:
return ":";
case SEMI:
return ";";
case DOT:
return ".";
case IDENTIFIER:
return "ident";
case FIELD_NAME:
return "fieldName";
case EOF:
return "EOF";
case SET:
return "Set";
case TREE_SET:
return "TreeSet";
case UNDEFINED:
return "undefined";
case HEX:
return "hex";
default:
return "Unknown";
}
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/ParseContext.java
================================================
package com.alibaba.fastjson.parser;
import java.lang.reflect.Type;
public class ParseContext {
public Object object;
public final ParseContext parent;
public final Object fieldName;
public final int level;
public Type type;
private transient String path;
public ParseContext(ParseContext parent, Object object, Object fieldName){
this.parent = parent;
this.object = object;
this.fieldName = fieldName;
this.level = parent == null ? 0 : parent.level + 1;
}
public String toString() {
if (path == null) {
if (parent == null) {
path = "$";
} else {
if (fieldName instanceof Integer) {
path = parent.toString() + "[" + fieldName + "]";
} else {
path = parent.toString() + "." + fieldName;
}
}
}
return path;
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/ParserConfig.java
================================================
/*
* Copyright 1999-2017 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.fastjson.parser;
import java.io.*;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.AccessControlException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import com.alibaba.fastjson.*;
import com.alibaba.fastjson.annotation.JSONCreator;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType;
import com.alibaba.fastjson.asm.ClassReader;
import com.alibaba.fastjson.asm.TypeCollector;
import com.alibaba.fastjson.parser.deserializer.*;
import com.alibaba.fastjson.serializer.*;
import com.alibaba.fastjson.spi.Module;
import com.alibaba.fastjson.support.moneta.MonetaCodec;
import com.alibaba.fastjson.util.*;
import com.alibaba.fastjson.util.IdentityHashMap;
import com.alibaba.fastjson.util.ServiceLoader;
import javax.xml.datatype.XMLGregorianCalendar;
import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_hashcode;
import static com.alibaba.fastjson.util.TypeUtils.fnv1a_64_magic_prime;
/**
* @author wenshao[szujobs@hotmail.com]
*/
public class ParserConfig {
public static final String DENY_PROPERTY_INTERNAL = "fastjson.parser.deny.internal";
public static final String DENY_PROPERTY = "fastjson.parser.deny";
public static final String AUTOTYPE_ACCEPT = "fastjson.parser.autoTypeAccept";
public static final String AUTOTYPE_SUPPORT_PROPERTY = "fastjson.parser.autoTypeSupport";
public static final String SAFE_MODE_PROPERTY = "fastjson.parser.safeMode";
public static final String[] DENYS_INTERNAL;
public static final String[] DENYS;
private static final String[] AUTO_TYPE_ACCEPT_LIST;
public static final boolean AUTO_SUPPORT;
public static final boolean SAFE_MODE;
private static final long[] INTERNAL_WHITELIST_HASHCODES;
static {
{
String property = IOUtils.getStringProperty(DENY_PROPERTY_INTERNAL);
DENYS_INTERNAL = splitItemsFormProperty(property);
}
{
String property = IOUtils.getStringProperty(DENY_PROPERTY);
DENYS = splitItemsFormProperty(property);
}
{
String property = IOUtils.getStringProperty(AUTOTYPE_SUPPORT_PROPERTY);
AUTO_SUPPORT = "true".equals(property);
}
{
String property = IOUtils.getStringProperty(SAFE_MODE_PROPERTY);
SAFE_MODE = "true".equals(property);
}
{
String property = IOUtils.getStringProperty(AUTOTYPE_ACCEPT);
String[] items = splitItemsFormProperty(property);
if (items == null) {
items = new String[0];
}
AUTO_TYPE_ACCEPT_LIST = items;
}
INTERNAL_WHITELIST_HASHCODES = new long[] {
0x9F2E20FB6049A371L,
0xA8AAA929446FFCE4L,
0xD45D6F8C9017FAL,
0x64DC636F343516DCL
};
}
public static ParserConfig getGlobalInstance() {
return global;
}
public static ParserConfig global = new ParserConfig();
private final IdentityHashMap deserializers = new IdentityHashMap();
private final IdentityHashMap> mixInDeserializers = new IdentityHashMap>(16);
private final ConcurrentMap> typeMapping = new ConcurrentHashMap>(16, 0.75f, 1);
private boolean asmEnable = !ASMUtils.IS_ANDROID;
public final SymbolTable symbolTable = new SymbolTable(4096);
public PropertyNamingStrategy propertyNamingStrategy;
protected ClassLoader defaultClassLoader;
protected ASMDeserializerFactory asmFactory;
private static boolean awtError = false;
private static boolean jdk8Error = false;
private static boolean jodaError = false;
private static boolean guavaError = false;
private boolean autoTypeSupport = AUTO_SUPPORT;
private long[] internalDenyHashCodes;
private long[] denyHashCodes;
private long[] acceptHashCodes;
public final boolean fieldBased;
private boolean jacksonCompatible = false;
public boolean compatibleWithJavaBean = TypeUtils.compatibleWithJavaBean;
private List modules = new ArrayList();
private volatile List autoTypeCheckHandlers;
private boolean safeMode = SAFE_MODE;
{
denyHashCodes = new long[]{
0x80D0C70BCC2FEA02L,
0x868385095A22725FL,
0x86FC2BF9BEAF7AEFL,
0x87F52A1B07EA33A6L,
0x8872F29FD0B0B7A7L,
0x8BAAEE8F9BF77FA7L,
0x8EADD40CB2A94443L,
0x8F75F9FA0DF03F80L,
0x9172A53F157930AFL,
0x92122D710E364FB8L,
0x941866E73BEFF4C9L,
0x94305C26580F73C5L,
0x9437792831DF7D3FL,
0xA123A62F93178B20L,
0xA85882CE1044C450L,
0xAA3DAFFDB10C4937L,
0xAAA9E6B7C1E1C6A7L,
0xAAAA0826487A3737L,
0xAB82562F53E6E48FL,
0xAC6262F52C98AA39L,
0xAD937A449831E8A0L,
0xAE50DA1FAD60A096L,
0xAFF6FF23388E225AL,
0xAFFF4C95B99A334DL,
0xB40F341C746EC94FL,
0xB7E8ED757F5D13A2L,
0xB98B6B5396932FE9L,
0xBCDD9DC12766F0CEL,
0xBCE0DEE34E726499L,
0xBE4F13E96A6796D0L,
0xBEBA72FB1CCBA426L,
0xC00BE1DEBAF2808BL,
0xC1086AFAE32E6258L,
0xC2664D0958ECFE4CL,
0xC41FF7C9C87C7C05L,
0xC664B363BACA050AL,
0xC7599EBFE3E72406L,
0xC8D49E5601E661A9L,
0xC8F04B3A28909935L,
0xC963695082FD728EL,
0xCBF29CE484222325L,
0xD1EFCDF4B3316D34L,
0xD54B91CC77B239EDL,
0xD59EE91F0B09EA01L,
0xD66F68AB92E7FEF5L,
0xD8CA3D595E982BACL,
0xDCD8D615A6449E3EL,
0xDE23A0809A8B9BD6L,
0xDEFC208F237D4104L,
0xDF2DDFF310CDB375L,
0xE09AE4604842582FL,
0xE1919804D5BF468FL,
0xE2EB3AC7E56C467EL,
0xE603D6A51FAD692BL,
0xE704FD19052B2A34L,
0xE9184BE55B1D962AL,
0xE9F20BAD25F60807L,
0xED13653CB45C4BEDL,
0xF2983D099D29B477L,
0xF3702A4A5490B8E8L,
0xF474E44518F26736L,
0xF4D93F4FB3E3D991L,
0xF5D77DCF8E4D71E6L,
0xF6C0340E73A36A69L,
0xF7E96E74DFA58DBCL,
0xFC773AE20C827691L,
0xFCF3E78644B98BD8L,
0xFD5BFC610056D720L,
0xFFA15BF021F1E37CL,
0xFFDD1A80F1ED3405L,
0x10E067CD55C5E5L,
0x761619136CC13EL,
0x22BAA234C5BFB8AL,
0x3085068CB7201B8L,
0x45B11BC78A3ABA3L,
0x55CFCA0F2281C07L,
0xA555C74FE3A5155L,
0xB6E292FA5955ADEL,
0xBEF8514D0B79293L,
0xEE6511B66FD5EF0L,
0x100150A253996624L,
0x10B2BDCA849D9B3EL,
0x10DBC48446E0DAE5L,
0x119B5B1F10210AFCL,
0x144277B467723158L,
0x14DB2E6FEAD04AF0L,
0x154B6CB22D294CFAL,
0x17924CCA5227622AL,
0x193B2697EAAED41AL,
0x1CD6F11C6A358BB7L,
0x1E0A8C3358FF3DAEL,
0x24652CE717E713BBL,
0x24D2F6048FEF4E49L,
0x24EC99D5E7DC5571L,
0x25E962F1C28F71A2L,
0x275D0732B877AF29L,
0x28AC82E44E933606L,
0x2A71CE2CC40A710CL,
0x2AD1CE3A112F015DL,
0x2ADFEFBBFE29D931L,
0x2B3A37467A344CDFL,
0x2B6DD8B3229D6837L,
0x2D308DBBC851B0D8L,
0x2FE950D3EA52AE0DL,
0x313BB4ABD8D4554CL,
0x327C8ED7C8706905L,
0x332F0B5369A18310L,
0x339A3E0B6BEEBEE9L,
0x33C64B921F523F2FL,
0x33E7F3E02571B153L,
0x34A81EE78429FDF1L,
0x37317698DCFCE894L,
0x378307CB0111E878L,
0x3826F4B2380C8B9BL,
0x398F942E01920CF0L,
0x3A31412DBB05C7FFL,
0x3A7EE0635EB2BC33L,
0x3ADBA40367F73264L,
0x3B0B51ECBF6DB221L,
0x3BF14094A524F0E2L,
0x42D11A560FC9FBA9L,
0x43320DC9D2AE0892L,
0x440E89208F445FB9L,
0x46C808A4B5841F57L,
0x470FD3A18BB39414L,
0x49312BDAFB0077D9L,
0x4A3797B30328202CL,
0x4BA3E254E758D70DL,
0x4BF881E49D37F530L,
0x4CF54EEC05E3E818L,
0x4DA972745FEB30C1L,
0x4EF08C90FF16C675L,
0x4FD10DDC6D13821FL,
0x521B4F573376DF4AL,
0x527DB6B46CE3BCBCL,
0x535E552D6F9700C1L,
0x54855E265FE1DAD5L,
0x5728504A6D454FFCL,
0x599B5C1213A099ACL,
0x5A5BD85C072E5EFEL,
0x5AB0CB3071AB40D1L,
0x5B6149820275EA42L,
0x5D74D3E5B9370476L,
0x5D92E6DDDE40ED84L,
0x5E61093EF8CDDDBBL,
0x5F215622FB630753L,
0x61C5BDD721385107L,
0x62DB241274397C34L,
0x636ECCA2A131B235L,
0x63A220E60A17C7B9L,
0x647AB0224E149EBEL,
0x65F81B84C1D920CDL,
0x665C53C311193973L,
0x6749835432E0F0D2L,
0x69B6E0175084B377L,
0x6A47501EBB2AFDB2L,
0x6FCABF6FA54CAFFFL,
0x6FE92D83FC0A4628L,
0x746BD4A53EC195FBL,
0x74B50BB9260E31FFL,
0x75CC60F5871D0FD3L,
0x767A586A5107FEEFL,
0x78E5935826671397L,
0x793ADDDED7A967F5L,
0x7AA7EE3627A19CF3L,
0x7AFA070241B8CC4BL,
0x7ED9311D28BF1A65L,
0x7ED9481D28BF417AL,
0x7EE6C477DA20BBE3L
};
long[] hashCodes = new long[AUTO_TYPE_ACCEPT_LIST.length];
for (int i = 0; i < AUTO_TYPE_ACCEPT_LIST.length; i++) {
hashCodes[i] = TypeUtils.fnv1a_64(AUTO_TYPE_ACCEPT_LIST[i]);
}
Arrays.sort(hashCodes);
acceptHashCodes = hashCodes;
}
public ParserConfig(){
this(false);
}
public ParserConfig(boolean fieldBase){
this(null, null, fieldBase);
}
public ParserConfig(ClassLoader parentClassLoader){
this(null, parentClassLoader, false);
}
public ParserConfig(ASMDeserializerFactory asmFactory){
this(asmFactory, null, false);
}
private ParserConfig(ASMDeserializerFactory asmFactory, ClassLoader parentClassLoader, boolean fieldBased){
this.fieldBased = fieldBased;
if (asmFactory == null && !ASMUtils.IS_ANDROID) {
try {
if (parentClassLoader == null) {
asmFactory = new ASMDeserializerFactory(new ASMClassLoader());
} else {
asmFactory = new ASMDeserializerFactory(parentClassLoader);
}
} catch (ExceptionInInitializerError error) {
// skip
} catch (AccessControlException error) {
// skip
} catch (NoClassDefFoundError error) {
// skip
}
}
this.asmFactory = asmFactory;
if (asmFactory == null) {
asmEnable = false;
}
initDeserializers();
addItemsToDeny(DENYS);
addItemsToDeny0(DENYS_INTERNAL);
addItemsToAccept(AUTO_TYPE_ACCEPT_LIST);
}
private final Callable initDeserializersWithJavaSql = new Callable() {
public Void call() {
deserializers.put(java.sql.Timestamp.class, SqlDateDeserializer.instance_timestamp);
deserializers.put(java.sql.Date.class, SqlDateDeserializer.instance);
deserializers.put(java.sql.Time.class, TimeDeserializer.instance);
deserializers.put(java.util.Date.class, DateCodec.instance);
return null;
}
};
private void initDeserializers() {
deserializers.put(SimpleDateFormat.class, MiscCodec.instance);
deserializers.put(Calendar.class, CalendarCodec.instance);
deserializers.put(XMLGregorianCalendar.class, CalendarCodec.instance);
deserializers.put(JSONObject.class, MapDeserializer.instance);
deserializers.put(JSONArray.class, CollectionCodec.instance);
deserializers.put(Map.class, MapDeserializer.instance);
deserializers.put(HashMap.class, MapDeserializer.instance);
deserializers.put(LinkedHashMap.class, MapDeserializer.instance);
deserializers.put(TreeMap.class, MapDeserializer.instance);
deserializers.put(ConcurrentMap.class, MapDeserializer.instance);
deserializers.put(ConcurrentHashMap.class, MapDeserializer.instance);
deserializers.put(Collection.class, CollectionCodec.instance);
deserializers.put(List.class, CollectionCodec.instance);
deserializers.put(ArrayList.class, CollectionCodec.instance);
deserializers.put(Object.class, JavaObjectDeserializer.instance);
deserializers.put(String.class, StringCodec.instance);
deserializers.put(StringBuffer.class, StringCodec.instance);
deserializers.put(StringBuilder.class, StringCodec.instance);
deserializers.put(char.class, CharacterCodec.instance);
deserializers.put(Character.class, CharacterCodec.instance);
deserializers.put(byte.class, NumberDeserializer.instance);
deserializers.put(Byte.class, NumberDeserializer.instance);
deserializers.put(short.class, NumberDeserializer.instance);
deserializers.put(Short.class, NumberDeserializer.instance);
deserializers.put(int.class, IntegerCodec.instance);
deserializers.put(Integer.class, IntegerCodec.instance);
deserializers.put(long.class, LongCodec.instance);
deserializers.put(Long.class, LongCodec.instance);
deserializers.put(BigInteger.class, BigIntegerCodec.instance);
deserializers.put(BigDecimal.class, BigDecimalCodec.instance);
deserializers.put(float.class, FloatCodec.instance);
deserializers.put(Float.class, FloatCodec.instance);
deserializers.put(double.class, NumberDeserializer.instance);
deserializers.put(Double.class, NumberDeserializer.instance);
deserializers.put(boolean.class, BooleanCodec.instance);
deserializers.put(Boolean.class, BooleanCodec.instance);
deserializers.put(Class.class, MiscCodec.instance);
deserializers.put(char[].class, new CharArrayCodec());
deserializers.put(AtomicBoolean.class, BooleanCodec.instance);
deserializers.put(AtomicInteger.class, IntegerCodec.instance);
deserializers.put(AtomicLong.class, LongCodec.instance);
deserializers.put(AtomicReference.class, ReferenceCodec.instance);
deserializers.put(WeakReference.class, ReferenceCodec.instance);
deserializers.put(SoftReference.class, ReferenceCodec.instance);
deserializers.put(UUID.class, MiscCodec.instance);
deserializers.put(TimeZone.class, MiscCodec.instance);
deserializers.put(Locale.class, MiscCodec.instance);
deserializers.put(Currency.class, MiscCodec.instance);
deserializers.put(Inet4Address.class, MiscCodec.instance);
deserializers.put(Inet6Address.class, MiscCodec.instance);
deserializers.put(InetSocketAddress.class, MiscCodec.instance);
deserializers.put(File.class, MiscCodec.instance);
deserializers.put(URI.class, MiscCodec.instance);
deserializers.put(URL.class, MiscCodec.instance);
deserializers.put(Pattern.class, MiscCodec.instance);
deserializers.put(Charset.class, MiscCodec.instance);
deserializers.put(JSONPath.class, MiscCodec.instance);
deserializers.put(Number.class, NumberDeserializer.instance);
deserializers.put(AtomicIntegerArray.class, AtomicCodec.instance);
deserializers.put(AtomicLongArray.class, AtomicCodec.instance);
deserializers.put(StackTraceElement.class, StackTraceElementDeserializer.instance);
deserializers.put(Serializable.class, JavaObjectDeserializer.instance);
deserializers.put(Cloneable.class, JavaObjectDeserializer.instance);
deserializers.put(Comparable.class, JavaObjectDeserializer.instance);
deserializers.put(Closeable.class, JavaObjectDeserializer.instance);
deserializers.put(JSONPObject.class, new JSONPDeserializer());
ModuleUtil.callWhenHasJavaSql(initDeserializersWithJavaSql);
}
private static String[] splitItemsFormProperty(final String property ){
if (property != null && property.length() > 0) {
return property.split(",");
}
return null;
}
public void configFromPropety(Properties properties) {
{
String property = properties.getProperty(DENY_PROPERTY);
String[] items = splitItemsFormProperty(property);
addItemsToDeny(items);
}
{
String property = properties.getProperty(AUTOTYPE_ACCEPT);
String[] items = splitItemsFormProperty(property);
addItemsToAccept(items);
}
{
String property = properties.getProperty(AUTOTYPE_SUPPORT_PROPERTY);
if ("true".equals(property)) {
this.autoTypeSupport = true;
} else if ("false".equals(property)) {
this.autoTypeSupport = false;
}
}
}
private void addItemsToDeny0(final String[] items){
if (items == null){
return;
}
for (int i = 0; i < items.length; ++i) {
String item = items[i];
this.addDenyInternal(item);
}
}
private void addItemsToDeny(final String[] items){
if (items == null){
return;
}
for (int i = 0; i < items.length; ++i) {
String item = items[i];
this.addDeny(item);
}
}
private void addItemsToAccept(final String[] items){
if (items == null){
return;
}
for (int i = 0; i < items.length; ++i) {
String item = items[i];
this.addAccept(item);
}
}
/**
* @since 1.2.68
*/
public boolean isSafeMode() {
return safeMode;
}
/**
* @since 1.2.68
*/
public void setSafeMode(boolean safeMode) {
this.safeMode = safeMode;
}
public boolean isAutoTypeSupport() {
return autoTypeSupport;
}
public void setAutoTypeSupport(boolean autoTypeSupport) {
this.autoTypeSupport = autoTypeSupport;
}
public boolean isAsmEnable() {
return asmEnable;
}
public void setAsmEnable(boolean asmEnable) {
this.asmEnable = asmEnable;
}
/**
* @deprecated
*/
public IdentityHashMap getDerializers() {
return deserializers;
}
public IdentityHashMap getDeserializers() {
return deserializers;
}
public ObjectDeserializer getDeserializer(Type type) {
ObjectDeserializer deserializer = get(type);
if (deserializer != null) {
return deserializer;
}
if (type instanceof Class>) {
return getDeserializer((Class>) type, type);
}
if (type instanceof ParameterizedType) {
Type rawType = ((ParameterizedType) type).getRawType();
if (rawType instanceof Class>) {
return getDeserializer((Class>) rawType, type);
} else {
return getDeserializer(rawType);
}
}
if (type instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type;
Type[] upperBounds = wildcardType.getUpperBounds();
if (upperBounds.length == 1) {
Type upperBoundType = upperBounds[0];
return getDeserializer(upperBoundType);
}
}
return JavaObjectDeserializer.instance;
}
public ObjectDeserializer getDeserializer(Class> clazz, Type type) {
ObjectDeserializer deserializer = get(type);
if (deserializer == null && type instanceof ParameterizedTypeImpl) {
Type innerType = TypeReference.intern((ParameterizedTypeImpl) type);
deserializer = get(innerType);
}
if (deserializer != null) {
return deserializer;
}
if (type == null) {
type = clazz;
}
deserializer = get(type);
if (deserializer != null) {
return deserializer;
}
{
JSONType annotation = TypeUtils.getAnnotation(clazz,JSONType.class);
if (annotation != null) {
Class> mappingTo = annotation.mappingTo();
if (mappingTo != Void.class) {
return getDeserializer(mappingTo, mappingTo);
}
}
}
if (type instanceof WildcardType || type instanceof TypeVariable || type instanceof ParameterizedType) {
deserializer = get(clazz);
}
if (deserializer != null) {
return deserializer;
}
for (Module module : modules) {
deserializer = module.createDeserializer(this, clazz);
if (deserializer != null) {
putDeserializer(type, deserializer);
return deserializer;
}
}
String className = clazz.getName();
className = className.replace('$', '.');
if (className.startsWith("java.awt.") //
&& AwtCodec.support(clazz)) {
if (!awtError) {
String[] names = new String[] {
"java.awt.Point",
"java.awt.Font",
"java.awt.Rectangle",
"java.awt.Color"
};
try {
for (String name : names) {
if (name.equals(className)) {
putDeserializer(Class.forName(name), deserializer = AwtCodec.instance);
return deserializer;
}
}
} catch (Throwable e) {
// skip
awtError = true;
}
deserializer = AwtCodec.instance;
}
}
if (!jdk8Error) {
try {
if (className.startsWith("java.time.")) {
String[] names = new String[] {
"java.time.LocalDateTime",
"java.time.LocalDate",
"java.time.LocalTime",
"java.time.ZonedDateTime",
"java.time.OffsetDateTime",
"java.time.OffsetTime",
"java.time.ZoneOffset",
"java.time.ZoneRegion",
"java.time.ZoneId",
"java.time.Period",
"java.time.Duration",
"java.time.Instant"
};
for (String name : names) {
if (name.equals(className)) {
putDeserializer(Class.forName(name), deserializer = Jdk8DateCodec.instance);
return deserializer;
}
}
} else if (className.startsWith("java.util.Optional")) {
String[] names = new String[] {
"java.util.Optional",
"java.util.OptionalDouble",
"java.util.OptionalInt",
"java.util.OptionalLong"
};
for (String name : names) {
if (name.equals(className)) {
putDeserializer(Class.forName(name), deserializer = OptionalCodec.instance);
return deserializer;
}
}
}
} catch (Throwable e) {
// skip
jdk8Error = true;
}
}
if (!jodaError) {
try {
if (className.startsWith("org.joda.time.")) {
String[] names = new String[] {
"org.joda.time.DateTime",
"org.joda.time.LocalDate",
"org.joda.time.LocalDateTime",
"org.joda.time.LocalTime",
"org.joda.time.Instant",
"org.joda.time.Period",
"org.joda.time.Duration",
"org.joda.time.DateTimeZone",
"org.joda.time.format.DateTimeFormatter"
};
for (String name : names) {
if (name.equals(className)) {
putDeserializer(Class.forName(name), deserializer = JodaCodec.instance);
return deserializer;
}
}
}
} catch (Throwable e) {
// skip
jodaError = true;
}
}
if ((!guavaError) //
&& className.startsWith("com.google.common.collect.")) {
try {
String[] names = new String[] {
"com.google.common.collect.HashMultimap",
"com.google.common.collect.LinkedListMultimap",
"com.google.common.collect.LinkedHashMultimap",
"com.google.common.collect.ArrayListMultimap",
"com.google.common.collect.TreeMultimap"
};
for (String name : names) {
if (name.equals(className)) {
putDeserializer(Class.forName(name), deserializer = GuavaCodec.instance);
return deserializer;
}
}
} catch (ClassNotFoundException e) {
// skip
guavaError = true;
}
}
if (className.equals("java.nio.ByteBuffer")) {
putDeserializer(clazz, deserializer = ByteBufferCodec.instance);
}
if (className.equals("java.nio.file.Path")) {
putDeserializer(clazz, deserializer = MiscCodec.instance);
}
if (clazz == Map.Entry.class) {
putDeserializer(clazz, deserializer = MiscCodec.instance);
}
if (className.equals("org.javamoney.moneta.Money")) {
putDeserializer(clazz, deserializer = MonetaCodec.instance);
}
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
for (AutowiredObjectDeserializer autowired : ServiceLoader.load(AutowiredObjectDeserializer.class,
classLoader)) {
for (Type forType : autowired.getAutowiredFor()) {
putDeserializer(forType, autowired);
}
}
} catch (Exception ex) {
// skip
}
if (deserializer == null) {
deserializer = get(type);
}
if (deserializer != null) {
return deserializer;
}
if (clazz.isEnum()) {
if (jacksonCompatible) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (TypeUtils.isJacksonCreator(method)) {
deserializer = createJavaBeanDeserializer(clazz, type);
putDeserializer(type, deserializer);
return deserializer;
}
}
}
Class mixInType = (Class) JSON.getMixInAnnotations(clazz);
Class> deserClass = null;
JSONType jsonType = TypeUtils.getAnnotation(mixInType != null ? mixInType : clazz, JSONType.class);
if (jsonType != null) {
deserClass = jsonType.deserializer();
try {
deserializer = (ObjectDeserializer) deserClass.newInstance();
putDeserializer(clazz, deserializer);
return deserializer;
} catch (Throwable error) {
// skip
}
}
Method jsonCreatorMethod = null;
if (mixInType != null) {
Method mixedCreator = getEnumCreator(mixInType, clazz);
if (mixedCreator != null) {
try {
jsonCreatorMethod = clazz.getMethod(mixedCreator.getName(), mixedCreator.getParameterTypes());
} catch (Exception e) {
// skip
}
}
} else {
jsonCreatorMethod = getEnumCreator(clazz, clazz);
}
if (jsonCreatorMethod != null) {
deserializer = new EnumCreatorDeserializer(jsonCreatorMethod);
putDeserializer(clazz, deserializer);
return deserializer;
}
deserializer = getEnumDeserializer(clazz);
} else if (clazz.isArray()) {
deserializer = ObjectArrayCodec.instance;
} else if (clazz == Set.class || clazz == HashSet.class || clazz == Collection.class || clazz == List.class
|| clazz == ArrayList.class) {
deserializer = CollectionCodec.instance;
} else if (Collection.class.isAssignableFrom(clazz)) {
deserializer = CollectionCodec.instance;
} else if (Map.class.isAssignableFrom(clazz)) {
deserializer = MapDeserializer.instance;
} else if (Throwable.class.isAssignableFrom(clazz)) {
deserializer = new ThrowableDeserializer(this, clazz);
} else if (PropertyProcessable.class.isAssignableFrom(clazz)) {
deserializer = new PropertyProcessableDeserializer((Class) clazz);
} else if (clazz == InetAddress.class) {
deserializer = MiscCodec.instance;
} else {
deserializer = createJavaBeanDeserializer(clazz, type);
}
putDeserializer(type, deserializer);
return deserializer;
}
private static Method getEnumCreator(Class clazz, Class enumClass) {
Method[] methods = clazz.getMethods();
Method jsonCreatorMethod = null;
for (Method method : methods) {
if (Modifier.isStatic(method.getModifiers())
&& method.getReturnType() == enumClass
&& method.getParameterTypes().length == 1
) {
JSONCreator jsonCreator = method.getAnnotation(JSONCreator.class);
if (jsonCreator != null) {
jsonCreatorMethod = method;
break;
}
}
}
return jsonCreatorMethod;
}
/**
* 可以通过重写这个方法,定义自己的枚举反序列化实现
* @param clazz 转换的类型
* @return 返回一个枚举的反序列化实现
* @author zhu.xiaojie
* @time 2020-4-5
*/
protected ObjectDeserializer getEnumDeserializer(Class> clazz){
return new EnumDeserializer(clazz);
}
/**
*
* @since 1.2.25
*/
public void initJavaBeanDeserializers(Class>... classes) {
if (classes == null) {
return;
}
for (Class> type : classes) {
if (type == null) {
continue;
}
ObjectDeserializer deserializer = createJavaBeanDeserializer(type, type);
putDeserializer(type, deserializer);
}
}
public ObjectDeserializer createJavaBeanDeserializer(Class> clazz, Type type) {
boolean asmEnable = this.asmEnable & !this.fieldBased;
if (asmEnable) {
JSONType jsonType = TypeUtils.getAnnotation(clazz,JSONType.class);
if (jsonType != null) {
Class> deserializerClass = jsonType.deserializer();
if (deserializerClass != Void.class) {
try {
Object deseralizer = deserializerClass.newInstance();
if (deseralizer instanceof ObjectDeserializer) {
return (ObjectDeserializer) deseralizer;
}
} catch (Throwable e) {
// skip
}
}
asmEnable = jsonType.asm()
&& jsonType.parseFeatures().length == 0;
}
if (asmEnable) {
Class> superClass = JavaBeanInfo.getBuilderClass(clazz, jsonType);
if (superClass == null) {
superClass = clazz;
}
for (;;) {
if (!Modifier.isPublic(superClass.getModifiers())) {
asmEnable = false;
break;
}
superClass = superClass.getSuperclass();
if (superClass == Object.class || superClass == null) {
break;
}
}
}
}
if (clazz.getTypeParameters().length != 0) {
asmEnable = false;
}
if (asmEnable && asmFactory != null && asmFactory.classLoader.isExternalClass(clazz)) {
asmEnable = false;
}
if (asmEnable) {
asmEnable = ASMUtils.checkName(clazz.getSimpleName());
}
if (asmEnable) {
if (clazz.isInterface()) {
asmEnable = false;
}
JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz
, type
, propertyNamingStrategy
,false
, TypeUtils.compatibleWithJavaBean
, jacksonCompatible
);
if (asmEnable && beanInfo.fields.length > 200) {
asmEnable = false;
}
Constructor> defaultConstructor = beanInfo.defaultConstructor;
if (asmEnable && defaultConstructor == null && !clazz.isInterface()) {
asmEnable = false;
}
for (FieldInfo fieldInfo : beanInfo.fields) {
if (fieldInfo.getOnly) {
asmEnable = false;
break;
}
Class> fieldClass = fieldInfo.fieldClass;
if (!Modifier.isPublic(fieldClass.getModifiers())) {
asmEnable = false;
break;
}
if (fieldClass.isMemberClass() && !Modifier.isStatic(fieldClass.getModifiers())) {
asmEnable = false;
break;
}
if (fieldInfo.getMember() != null //
&& !ASMUtils.checkName(fieldInfo.getMember().getName())) {
asmEnable = false;
break;
}
JSONField annotation = fieldInfo.getAnnotation();
if (annotation != null //
&& ((!ASMUtils.checkName(annotation.name())) //
|| annotation.format().length() != 0 //
|| annotation.deserializeUsing() != Void.class //
|| annotation.parseFeatures().length != 0 //
|| annotation.unwrapped())
|| (fieldInfo.method != null && fieldInfo.method.getParameterTypes().length > 1)) {
asmEnable = false;
break;
}
if (fieldClass.isEnum()) { // EnumDeserializer
ObjectDeserializer fieldDeser = this.getDeserializer(fieldClass);
if (!(fieldDeser instanceof EnumDeserializer)) {
asmEnable = false;
break;
}
}
}
}
if (asmEnable) {
if (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) {
asmEnable = false;
}
}
if (asmEnable) {
if (TypeUtils.isXmlField(clazz)) {
asmEnable = false;
}
}
if (!asmEnable) {
return new JavaBeanDeserializer(this, clazz, type);
}
JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, type, propertyNamingStrategy);
try {
return asmFactory.createJavaBeanDeserializer(this, beanInfo);
// } catch (VerifyError e) {
// e.printStackTrace();
// return new JavaBeanDeserializer(this, clazz, type);
} catch (NoSuchMethodException ex) {
return new JavaBeanDeserializer(this, clazz, type);
} catch (JSONException asmError) {
return new JavaBeanDeserializer(this, beanInfo);
} catch (Exception e) {
throw new JSONException("create asm deserializer error, " + clazz.getName(), e);
}
}
public FieldDeserializer createFieldDeserializer(ParserConfig mapping, //
JavaBeanInfo beanInfo, //
FieldInfo fieldInfo) {
Class> clazz = beanInfo.clazz;
Class> fieldClass = fieldInfo.fieldClass;
Class> deserializeUsing = null;
JSONField annotation = fieldInfo.getAnnotation();
if (annotation != null) {
deserializeUsing = annotation.deserializeUsing();
if (deserializeUsing == Void.class) {
deserializeUsing = null;
}
}
if (deserializeUsing == null && (fieldClass == List.class || fieldClass == ArrayList.class)) {
return new ArrayListTypeFieldDeserializer(mapping, clazz, fieldInfo);
}
return new DefaultFieldDeserializer(mapping, clazz, fieldInfo);
}
public void putDeserializer(Type type, ObjectDeserializer deserializer) {
Type mixin = JSON.getMixInAnnotations(type);
if (mixin != null) {
IdentityHashMap mixInClasses = this.mixInDeserializers.get(type);
if (mixInClasses == null) {
//多线程下可能会重复创建,但不影响正确性
mixInClasses = new IdentityHashMap(4);
this.mixInDeserializers.put(type, mixInClasses);
}
mixInClasses.put(mixin, deserializer);
} else {
this.deserializers.put(type, deserializer);
}
}
public ObjectDeserializer get(Type type) {
Type mixin = JSON.getMixInAnnotations(type);
if (null == mixin) {
return this.deserializers.get(type);
}
IdentityHashMap mixInClasses = this.mixInDeserializers.get(type);
if (mixInClasses == null) {
return null;
}
return mixInClasses.get(mixin);
}
public ObjectDeserializer getDeserializer(FieldInfo fieldInfo) {
return getDeserializer(fieldInfo.fieldClass, fieldInfo.fieldType);
}
/**
* @deprecated internal method, dont call
*/
public boolean isPrimitive(Class> clazz) {
return isPrimitive2(clazz);
}
private static Function, Boolean> isPrimitiveFuncation = new Function, Boolean>() {
public Boolean apply(Class> clazz) {
return clazz == java.sql.Date.class //
|| clazz == java.sql.Time.class //
|| clazz == java.sql.Timestamp.class;
}
};
/**
* @deprecated internal method, dont call
*/
public static boolean isPrimitive2(final Class> clazz) {
Boolean primitive = clazz.isPrimitive() //
|| clazz == Boolean.class //
|| clazz == Character.class //
|| clazz == Byte.class //
|| clazz == Short.class //
|| clazz == Integer.class //
|| clazz == Long.class //
|| clazz == Float.class //
|| clazz == Double.class //
|| clazz == BigInteger.class //
|| clazz == BigDecimal.class //
|| clazz == String.class //
|| clazz == java.util.Date.class //
|| clazz.isEnum() //
;
if (!primitive) {
primitive = ModuleUtil.callWhenHasJavaSql(isPrimitiveFuncation, clazz);
}
return primitive != null ? primitive : false;
}
/**
* fieldName,field ,先生成fieldName的快照,减少之后的findField的轮询
*
* @param clazz
* @param fieldCacheMap :map<fieldName ,Field>
*/
public static void parserAllFieldToCache(Class> clazz,Map**fieldName*/String , Field> fieldCacheMap){
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
if (!fieldCacheMap.containsKey(fieldName)) {
fieldCacheMap.put(fieldName, field);
}
}
if (clazz.getSuperclass() != null && clazz.getSuperclass() != Object.class) {
parserAllFieldToCache(clazz.getSuperclass(), fieldCacheMap);
}
}
public static Field getFieldFromCache(String fieldName, Map fieldCacheMap) {
Field field = fieldCacheMap.get(fieldName);
if (field == null) {
field = fieldCacheMap.get("_" + fieldName);
}
if (field == null) {
field = fieldCacheMap.get("m_" + fieldName);
}
if (field == null) {
char c0 = fieldName.charAt(0);
if (c0 >= 'a' && c0 <= 'z') {
char[] chars = fieldName.toCharArray();
chars[0] -= 32; // lower
String fieldNameX = new String(chars);
field = fieldCacheMap.get(fieldNameX);
}
if (fieldName.length() > 2) {
char c1 = fieldName.charAt(1);
if (c0 >= 'a' && c0 <= 'z'
&& c1 >= 'A' && c1 <= 'Z') {
for (Map.Entry entry : fieldCacheMap.entrySet()) {
if (fieldName.equalsIgnoreCase(entry.getKey())) {
field = entry.getValue();
break;
}
}
}
}
}
return field;
}
public ClassLoader getDefaultClassLoader() {
return defaultClassLoader;
}
public void setDefaultClassLoader(ClassLoader defaultClassLoader) {
this.defaultClassLoader = defaultClassLoader;
}
public void addDenyInternal(String name) {
if (name == null || name.length() == 0) {
return;
}
long hash = TypeUtils.fnv1a_64(name);
if (internalDenyHashCodes == null) {
this.internalDenyHashCodes = new long[] {hash};
return;
}
if (Arrays.binarySearch(this.internalDenyHashCodes, hash) >= 0) {
return;
}
long[] hashCodes = new long[this.internalDenyHashCodes.length + 1];
hashCodes[hashCodes.length - 1] = hash;
System.arraycopy(this.internalDenyHashCodes, 0, hashCodes, 0, this.internalDenyHashCodes.length);
Arrays.sort(hashCodes);
this.internalDenyHashCodes = hashCodes;
}
public void addDeny(String name) {
if (name == null || name.length() == 0) {
return;
}
long hash = TypeUtils.fnv1a_64(name);
if (Arrays.binarySearch(this.denyHashCodes, hash) >= 0) {
return;
}
long[] hashCodes = new long[this.denyHashCodes.length + 1];
hashCodes[hashCodes.length - 1] = hash;
System.arraycopy(this.denyHashCodes, 0, hashCodes, 0, this.denyHashCodes.length);
Arrays.sort(hashCodes);
this.denyHashCodes = hashCodes;
}
public void addAccept(String name) {
if (name == null || name.length() == 0) {
return;
}
long hash = TypeUtils.fnv1a_64(name);
if (Arrays.binarySearch(this.acceptHashCodes, hash) >= 0) {
return;
}
long[] hashCodes = new long[this.acceptHashCodes.length + 1];
hashCodes[hashCodes.length - 1] = hash;
System.arraycopy(this.acceptHashCodes, 0, hashCodes, 0, this.acceptHashCodes.length);
Arrays.sort(hashCodes);
this.acceptHashCodes = hashCodes;
}
public Class> checkAutoType(Class type) {
if (get(type) != null) {
return type;
}
return checkAutoType(type.getName(), null, JSON.DEFAULT_PARSER_FEATURE);
}
public Class> checkAutoType(String typeName, Class> expectClass) {
return checkAutoType(typeName, expectClass, JSON.DEFAULT_PARSER_FEATURE);
}
public Class> checkAutoType(String typeName, Class> expectClass, int features) {
if (typeName == null) {
return null;
}
if (autoTypeCheckHandlers != null) {
for (AutoTypeCheckHandler h : autoTypeCheckHandlers) {
Class> type = h.handler(typeName, expectClass, features);
if (type != null) {
return type;
}
}
}
final int safeModeMask = Feature.SafeMode.mask;
boolean safeMode = this.safeMode
|| (features & safeModeMask) != 0
|| (JSON.DEFAULT_PARSER_FEATURE & safeModeMask) != 0;
if (safeMode) {
throw new JSONException("safeMode not support autoType : " + typeName);
}
final int mask = Feature.SupportAutoType.mask;
boolean autoTypeSupport = this.autoTypeSupport
|| (features & mask) != 0
|| (JSON.DEFAULT_PARSER_FEATURE & mask) != 0;
if (typeName.length() >= 192 || typeName.length() < 3) {
throw new JSONException("autoType is not support. " + typeName);
}
final boolean expectClassFlag;
if (expectClass == null) {
expectClassFlag = false;
} else {
long expectHash = TypeUtils.fnv1a_64(expectClass.getName());
if (expectHash == 0x90a25f5baa21529eL
|| expectHash == 0x2d10a5801b9d6136L
|| expectHash == 0xaf586a571e302c6bL
|| expectHash == 0xed007300a7b227c6L
|| expectHash == 0x295c4605fd1eaa95L
|| expectHash == 0x47ef269aadc650b4L
|| expectHash == 0x6439c4dff712ae8bL
|| expectHash == 0xe3dd9875a2dc5283L
|| expectHash == 0xe2a8ddba03e69e0dL
|| expectHash == 0xd734ceb4c3e9d1daL
) {
expectClassFlag = false;
} else {
expectClassFlag = true;
}
}
String className = typeName.replace('$', '.');
Class> clazz;
final long h1 = (fnv1a_64_magic_hashcode ^ className.charAt(0)) * fnv1a_64_magic_prime;
if (h1 == 0xaf64164c86024f1aL) { // [
throw new JSONException("autoType is not support. " + typeName);
}
if ((h1 ^ className.charAt(className.length() - 1)) * fnv1a_64_magic_prime == 0x9198507b5af98f0L) {
throw new JSONException("autoType is not support. " + typeName);
}
final long h3 = (((((fnv1a_64_magic_hashcode ^ className.charAt(0))
* fnv1a_64_magic_prime)
^ className.charAt(1))
* fnv1a_64_magic_prime)
^ className.charAt(2))
* fnv1a_64_magic_prime;
long fullHash = TypeUtils.fnv1a_64(className);
boolean internalWhite = Arrays.binarySearch(INTERNAL_WHITELIST_HASHCODES, fullHash) >= 0;
if (internalDenyHashCodes != null) {
long hash = h3;
for (int i = 3; i < className.length(); ++i) {
hash ^= className.charAt(i);
hash *= fnv1a_64_magic_prime;
if (Arrays.binarySearch(internalDenyHashCodes, hash) >= 0) {
throw new JSONException("autoType is not support. " + typeName);
}
}
}
if ((!internalWhite) && (autoTypeSupport || expectClassFlag)) {
long hash = h3;
for (int i = 3; i < className.length(); ++i) {
hash ^= className.charAt(i);
hash *= fnv1a_64_magic_prime;
if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);
if (clazz != null) {
return clazz;
}
}
if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) {
if (Arrays.binarySearch(acceptHashCodes, fullHash) >= 0) {
continue;
}
throw new JSONException("autoType is not support. " + typeName);
}
}
}
clazz = TypeUtils.getClassFromMapping(typeName);
if (clazz == null) {
clazz = deserializers.findClass(typeName);
}
if (expectClass == null && clazz != null && Throwable.class.isAssignableFrom(clazz) && !autoTypeSupport) {
clazz = null;
}
if (clazz == null) {
clazz = typeMapping.get(typeName);
}
if (internalWhite) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);
}
if (clazz != null) {
if (expectClass != null
&& clazz != java.util.HashMap.class
&& clazz != java.util.LinkedHashMap.class
&& !expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
return clazz;
}
if (!autoTypeSupport) {
long hash = h3;
for (int i = 3; i < className.length(); ++i) {
char c = className.charAt(i);
hash ^= c;
hash *= fnv1a_64_magic_prime;
if (Arrays.binarySearch(denyHashCodes, hash) >= 0) {
if (typeName.endsWith("Exception") || typeName.endsWith("Error")) {
return null;
}
throw new JSONException("autoType is not support. " + typeName);
}
// white list
if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);
if (clazz == null) {
return expectClass;
}
if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
return clazz;
}
}
}
boolean jsonType = false;
InputStream is = null;
try {
String resource = typeName.replace('.', '/') + ".class";
if (defaultClassLoader != null) {
is = defaultClassLoader.getResourceAsStream(resource);
} else {
is = ParserConfig.class.getClassLoader().getResourceAsStream(resource);
}
if (is != null) {
ClassReader classReader = new ClassReader(is, true);
TypeCollector visitor = new TypeCollector("", new Class[0]);
classReader.accept(visitor);
jsonType = visitor.hasJsonType();
}
} catch (Exception e) {
// skip
} finally {
IOUtils.close(is);
}
if (autoTypeSupport || jsonType || expectClassFlag) {
boolean cacheClass = autoTypeSupport || jsonType;
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
}
if (clazz != null) {
if (jsonType) {
if (autoTypeSupport) {
TypeUtils.addMapping(typeName, clazz);
}
return clazz;
}
if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
|| javax.sql.DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
|| javax.sql.RowSet.class.isAssignableFrom(clazz) //
) {
throw new JSONException("autoType is not support. " + typeName);
}
if (expectClass != null) {
if (expectClass.isAssignableFrom(clazz)) {
if (autoTypeSupport) {
TypeUtils.addMapping(typeName, clazz);
}
return clazz;
} else {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
}
JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, clazz, propertyNamingStrategy);
if (beanInfo.creatorConstructor != null && autoTypeSupport) {
throw new JSONException("autoType is not support. " + typeName);
}
}
if (!autoTypeSupport) {
if (typeName.endsWith("Exception") || typeName.endsWith("Error")) {
return null;
}
throw new JSONException("autoType is not support. " + typeName);
}
if (clazz != null) {
if (autoTypeSupport) {
TypeUtils.addMapping(typeName, clazz);
}
}
return clazz;
}
public void clearDeserializers() {
this.deserializers.clear();
this.initDeserializers();
}
public boolean isJacksonCompatible() {
return jacksonCompatible;
}
public void setJacksonCompatible(boolean jacksonCompatible) {
this.jacksonCompatible = jacksonCompatible;
}
public void register(String typeName, Class type) {
typeMapping.putIfAbsent(typeName, type);
}
public void register(Module module) {
this.modules.add(module);
}
public void addAutoTypeCheckHandler(AutoTypeCheckHandler h) {
List autoTypeCheckHandlers = this.autoTypeCheckHandlers;
if (autoTypeCheckHandlers == null) {
this.autoTypeCheckHandlers
= autoTypeCheckHandlers
= new CopyOnWriteArrayList();
}
autoTypeCheckHandlers.add(h);
}
/**
* @since 1.2.68
*/
public interface AutoTypeCheckHandler {
Class> handler(String typeName, Class> expectClass, int features);
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/SymbolTable.java
================================================
/*
* Copyright 1999-2017 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.fastjson.parser;
import com.alibaba.fastjson.JSON;
/**
* @author wenshao[szujobs@hotmail.com]
*/
public class SymbolTable {
private final String[] symbols;
private final int indexMask;
public SymbolTable(int tableSize){
this.indexMask = tableSize - 1;
this.symbols = new String[tableSize];
this.addSymbol("$ref", 0, 4, "$ref".hashCode());
this.addSymbol(JSON.DEFAULT_TYPE_KEY, 0, JSON.DEFAULT_TYPE_KEY.length(), JSON.DEFAULT_TYPE_KEY.hashCode());
}
public String addSymbol(char[] buffer, int offset, int len) {
// search for identical symbol
int hash = hash(buffer, offset, len);
return addSymbol(buffer, offset, len, hash);
}
/**
* Adds the specified symbol to the symbol table and returns a reference to the unique symbol. If the symbol already
* exists, the previous symbol reference is returned instead, in order guarantee that symbol references remain
* unique.
*
* @param buffer The buffer containing the new symbol.
* @param offset The offset into the buffer of the new symbol.
* @param len The length of the new symbol in the buffer.
*/
public String addSymbol(char[] buffer, int offset, int len, int hash) {
final int bucket = hash & indexMask;
String symbol = symbols[bucket];
if (symbol != null) {
boolean eq = true;
if (hash == symbol.hashCode() //
&& len == symbol.length()) {
for (int i = 0; i < len; i++) {
if (buffer[offset + i] != symbol.charAt(i)) {
eq = false;
break;
}
}
} else {
eq = false;
}
if (eq) {
return symbol;
} else {
return new String(buffer, offset, len);
}
}
symbol = new String(buffer, offset, len).intern();
symbols[bucket] = symbol;
return symbol;
}
public String addSymbol(String buffer, int offset, int len, int hash) {
return addSymbol(buffer, offset, len, hash, false);
}
public String addSymbol(String buffer, int offset, int len, int hash, boolean replace) {
final int bucket = hash & indexMask;
String symbol = symbols[bucket];
if (symbol != null) {
if (hash == symbol.hashCode() //
&& len == symbol.length() //
&& buffer.startsWith(symbol, offset)) {
return symbol;
}
String str = subString(buffer, offset, len);
if (replace) {
symbols[bucket] = str;
}
return str;
}
symbol = len == buffer.length() //
? buffer //
: subString(buffer, offset, len);
symbol = symbol.intern();
symbols[bucket] = symbol;
return symbol;
}
private static String subString(String src, int offset, int len) {
char[] chars = new char[len];
src.getChars(offset, offset + len, chars, 0);
return new String(chars);
}
public static int hash(char[] buffer, int offset, int len) {
int h = 0;
int off = offset;
for (int i = 0; i < len; i++) {
h = 31 * h + buffer[off++];
}
return h;
}
}
================================================
FILE: src/main/java/com/alibaba/fastjson/parser/deserializer/ASMDeserializerFactory.java
================================================
package com.alibaba.fastjson.parser.deserializer;
import static com.alibaba.fastjson.util.ASMUtils.desc;
import static com.alibaba.fastjson.util.ASMUtils.type;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicLong;
import com.alibaba.fastjson.asm.ClassWriter;
import com.alibaba.fastjson.asm.FieldWriter;
import com.alibaba.fastjson.asm.Label;
import com.alibaba.fastjson.asm.MethodVisitor;
import com.alibaba.fastjson.asm.MethodWriter;
import com.alibaba.fastjson.asm.Opcodes;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.DefaultJSONParser.ResolveTask;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONLexerBase;
import com.alibaba.fastjson.parser.JSONToken;
import com.alibaba.fastjson.parser.ParseContext;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.parser.SymbolTable;
import com.alibaba.fastjson.util.*;
public class ASMDeserializerFactory implements Opcodes {
public final ASMClassLoader classLoader;
protected final AtomicLong seed = new AtomicLong();
final static String DefaultJSONParser = type(DefaultJSONParser.class);
final static String JSONLexerBase = type(JSONLexerBase.class);
public ASMDeserializerFactory(ClassLoader parentClassLoader){
classLoader = parentClassLoader instanceof ASMClassLoader //
? (ASMClassLoader) parentClassLoader //
: new ASMClassLoader(parentClassLoader);
}
public ObjectDeserializer createJavaBeanDeserializer(ParserConfig config, JavaBeanInfo beanInfo) throws Exception {
Class> clazz = beanInfo.clazz;
if (clazz.isPrimitive()) {
throw new IllegalArgumentException("not support type :" + clazz.getName());
}
String className = "FastjsonASMDeserializer_" + seed.incrementAndGet() + "_" + clazz.getSimpleName();
String classNameType;
String classNameFull;
Package pkg = ASMDeserializerFactory.class.getPackage();
if (pkg != null) {
String packageName = pkg.getName();
classNameType = packageName.replace('.', '/') + "/" + className;
classNameFull = packageName + "." + className;
} else {
classNameType = className;
classNameFull = className;
}
ClassWriter cw = new ClassWriter();
cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, classNameType, type(JavaBeanDeserializer.class), null);
_init(cw, new Context(classNameType, config, beanInfo, 3));
_createInstance(cw, new Context(classNameType, config, beanInfo, 3));
_deserialze(cw, new Context(classNameType, config, beanInfo, 5));
_deserialzeArrayMapping(cw, new Context(classNameType, config, beanInfo, 4));
byte[] code = cw.toByteArray();
Class> deserClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length);
Constructor> constructor = deserClass.getConstructor(ParserConfig.class, JavaBeanInfo.class);
Object instance = constructor.newInstance(config, beanInfo);
return (ObjectDeserializer) instance;
}
private void _setFlag(MethodVisitor mw, Context context, int i) {
String varName = "_asm_flag_" + (i / 32);
mw.visitVarInsn(ILOAD, context.var(varName));
mw.visitLdcInsn(1 << i);
mw.visitInsn(IOR);
mw.visitVarInsn(ISTORE, context.var(varName));
}
private void _isFlag(MethodVisitor mw, Context context, int i, Label label) {
mw.visitVarInsn(ILOAD, context.var("_asm_flag_" + (i / 32)));
mw.visitLdcInsn(1 << i);
mw.visitInsn(IAND);
mw.visitJumpInsn(IFEQ, label);
}
private void _deserialzeArrayMapping(ClassWriter cw, Context context) {
MethodVisitor mw = new MethodWriter(cw, ACC_PUBLIC, "deserialzeArrayMapping",
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
null, null);
defineVarLexer(context, mw);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 1);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getSymbolTable", "()" + desc(SymbolTable.class));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanTypeName", "(" + desc(SymbolTable.class) + ")Ljava/lang/String;");
mw.visitVarInsn(ASTORE, context.var("typeName"));
Label typeNameNotNull_ = new Label();
mw.visitVarInsn(ALOAD, context.var("typeName"));
mw.visitJumpInsn(IFNULL, typeNameNotNull_);
mw.visitVarInsn(ALOAD, 1);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getConfig", "()" + desc(ParserConfig.class));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, type(JavaBeanDeserializer.class), "beanInfo", desc(JavaBeanInfo.class));
mw.visitVarInsn(ALOAD, context.var("typeName"));
mw.visitMethodInsn(INVOKESTATIC, type(JavaBeanDeserializer.class), "getSeeAlso"
, "(" + desc(ParserConfig.class) + desc(JavaBeanInfo.class) + "Ljava/lang/String;)" + desc(JavaBeanDeserializer.class));
mw.visitVarInsn(ASTORE, context.var("userTypeDeser"));
mw.visitVarInsn(ALOAD, context.var("userTypeDeser"));
mw.visitTypeInsn(INSTANCEOF, type(JavaBeanDeserializer.class));
mw.visitJumpInsn(IFEQ, typeNameNotNull_);
mw.visitVarInsn(ALOAD, context.var("userTypeDeser"));
mw.visitVarInsn(ALOAD, Context.parser);
mw.visitVarInsn(ALOAD, 2);
mw.visitVarInsn(ALOAD, 3);
mw.visitVarInsn(ALOAD, 4);
mw.visitMethodInsn(INVOKEVIRTUAL, //
type(JavaBeanDeserializer.class), //
"deserialzeArrayMapping", //
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
mw.visitInsn(ARETURN);
mw.visitLabel(typeNameNotNull_);
_createInstance(context, mw);
FieldInfo[] sortedFieldInfoList = context.beanInfo.sortedFields;
int fieldListSize = sortedFieldInfoList.length;
for (int i = 0; i < fieldListSize; ++i) {
final boolean last = (i == fieldListSize - 1);
final char seperator = last ? ']' : ',';
FieldInfo fieldInfo = sortedFieldInfoList[i];
Class> fieldClass = fieldInfo.fieldClass;
Type fieldType = fieldInfo.fieldType;
if (fieldClass == byte.class //
|| fieldClass == short.class //
|| fieldClass == int.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanInt", "(C)I");
mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Byte.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanInt", "(C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == Short.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanInt", "(C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == Integer.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanInt", "(C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == long.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanLong", "(C)J");
mw.visitVarInsn(LSTORE, context.var_asm(fieldInfo, 2));
} else if (fieldClass == Long.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanLong", "(C)J");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == boolean.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanBoolean", "(C)Z");
mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == float.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFloat", "(C)F");
mw.visitVarInsn(FSTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Float.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFloat", "(C)F");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == double.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanDouble", "(C)D");
mw.visitVarInsn(DSTORE, context.var_asm(fieldInfo, 2));
} else if (fieldClass == Double.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanDouble", "(C)D");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == char.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanString", "(C)Ljava/lang/String;");
mw.visitInsn(ICONST_0);
mw.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C");
mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == String.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanString", "(C)Ljava/lang/String;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == BigDecimal.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanDecimal", "(C)Ljava/math/BigDecimal;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == java.util.Date.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanDate", "(C)Ljava/util/Date;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == java.util.UUID.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanUUID", "(C)Ljava/util/UUID;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass.isEnum()) {
Label enumNumIf_ = new Label();
Label enumNumErr_ = new Label();
Label enumStore_ = new Label();
Label enumQuote_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "getCurrent", "()C");
mw.visitInsn(DUP);
mw.visitVarInsn(ISTORE, context.var("ch"));
mw.visitLdcInsn((int) 'n');
mw.visitJumpInsn(IF_ICMPEQ, enumQuote_);
mw.visitVarInsn(ILOAD, context.var("ch"));
mw.visitLdcInsn((int) '\"');
mw.visitJumpInsn(IF_ICMPNE, enumNumIf_);
mw.visitLabel(enumQuote_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(fieldClass)));
mw.visitVarInsn(ALOAD, 1);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getSymbolTable", "()" + desc(SymbolTable.class));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanEnum",
"(Ljava/lang/Class;" + desc(SymbolTable.class) + "C)Ljava/lang/Enum;");
mw.visitJumpInsn(GOTO, enumStore_);
// (ch >= '0' && ch <= '9') {
mw.visitLabel(enumNumIf_);
mw.visitVarInsn(ILOAD, context.var("ch"));
mw.visitLdcInsn((int) '0');
mw.visitJumpInsn(IF_ICMPLT, enumNumErr_);
mw.visitVarInsn(ILOAD, context.var("ch"));
mw.visitLdcInsn((int) '9');
mw.visitJumpInsn(IF_ICMPGT, enumNumErr_);
_getFieldDeser(context, mw, fieldInfo);
mw.visitTypeInsn(CHECKCAST, type(EnumDeserializer.class)); // cast
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanInt", "(C)I");
mw.visitMethodInsn(INVOKEVIRTUAL, type(EnumDeserializer.class), "valueOf", "(I)Ljava/lang/Enum;");
mw.visitJumpInsn(GOTO, enumStore_);
mw.visitLabel(enumNumErr_);
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, type(JavaBeanDeserializer.class), "scanEnum",
"(L" + JSONLexerBase + ";C)Ljava/lang/Enum;");
mw.visitLabel(enumStore_);
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (Collection.class.isAssignableFrom(fieldClass)) {
Class> itemClass = TypeUtils.getCollectionItemClass(fieldType);
if (itemClass == String.class) {
if (fieldClass == List.class
|| fieldClass == Collections.class
|| fieldClass == ArrayList.class
) {
mw.visitTypeInsn(NEW, type(ArrayList.class));
mw.visitInsn(DUP);
mw.visitMethodInsn(INVOKESPECIAL, type(ArrayList.class), "", "()V");
} else {
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(fieldClass)));
mw.visitMethodInsn(INVOKESTATIC, type(TypeUtils.class), "createCollection",
"(Ljava/lang/Class;)Ljava/util/Collection;");
}
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanStringArray", "(Ljava/util/Collection;C)V");
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else {
Label notError_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "token", "()I");
mw.visitVarInsn(ISTORE, context.var("token"));
mw.visitVarInsn(ILOAD, context.var("token"));
int token = i == 0 ? JSONToken.LBRACKET : JSONToken.COMMA;
mw.visitLdcInsn(token);
mw.visitJumpInsn(IF_ICMPEQ, notError_);
mw.visitVarInsn(ALOAD, 1); // DefaultJSONParser
mw.visitLdcInsn(token);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "throwException", "(I)V");
mw.visitLabel(notError_);
Label quickElse_ = new Label(), quickEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "getCurrent", "()C");
mw.visitVarInsn(BIPUSH, '[');
mw.visitJumpInsn(IF_ICMPNE, quickElse_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "next", "()C");
mw.visitInsn(POP);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.LBRACKET);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "setToken", "(I)V");
mw.visitJumpInsn(GOTO, quickEnd_);
mw.visitLabel(quickElse_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.LBRACKET);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "nextToken", "(I)V");
mw.visitLabel(quickEnd_);
_newCollection(mw, fieldClass, i, false);
mw.visitInsn(DUP);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
_getCollectionFieldItemDeser(context, mw, fieldInfo, itemClass);
mw.visitVarInsn(ALOAD, 1);
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(itemClass)));
mw.visitVarInsn(ALOAD, 3);
mw.visitMethodInsn(INVOKESTATIC, type(JavaBeanDeserializer.class),
"parseArray",
"(Ljava/util/Collection;" //
+ desc(ObjectDeserializer.class) //
+ "L" + DefaultJSONParser + ";" //
+ "Ljava/lang/reflect/Type;Ljava/lang/Object;)V");
}
} else if (fieldClass.isArray()) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONToken.LBRACKET);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "nextToken", "(I)V");
mw.visitVarInsn(ALOAD, Context.parser);
mw.visitVarInsn(ALOAD, 0);
mw.visitLdcInsn(i);
mw.visitMethodInsn(INVOKEVIRTUAL, type(JavaBeanDeserializer.class), "getFieldType",
"(I)Ljava/lang/reflect/Type;");
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "parseObject",
"(Ljava/lang/reflect/Type;)Ljava/lang/Object;");
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else {
Label objElseIf_ = new Label();
Label objEndIf_ = new Label();
if (fieldClass == java.util.Date.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "getCurrent", "()C");
mw.visitLdcInsn((int) '1');
mw.visitJumpInsn(IF_ICMPNE, objElseIf_);
mw.visitTypeInsn(NEW, type(java.util.Date.class));
mw.visitInsn(DUP);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanLong", "(C)J");
mw.visitMethodInsn(INVOKESPECIAL, type(java.util.Date.class), "", "(J)V");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitJumpInsn(GOTO, objEndIf_);
}
mw.visitLabel(objElseIf_);
_quickNextToken(context, mw, JSONToken.LBRACKET);
_deserObject(context, mw, fieldInfo, fieldClass, i);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "token", "()I");
mw.visitLdcInsn(JSONToken.RBRACKET);
mw.visitJumpInsn(IF_ICMPEQ, objEndIf_);
// mw.visitInsn(POP);
// mw.visitInsn(POP);
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, context.var("lexer"));
if (!last) {
mw.visitLdcInsn(JSONToken.COMMA);
} else {
mw.visitLdcInsn(JSONToken.RBRACKET);
}
mw.visitMethodInsn(INVOKESPECIAL, //
type(JavaBeanDeserializer.class), //
"check", "(" + desc(JSONLexer.class) + "I)V");
mw.visitLabel(objEndIf_);
continue;
}
}
_batchSet(context, mw, false);
Label quickElse_ = new Label(), quickElseIf_ = new Label(), quickElseIfEOI_ = new Label(),
quickEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "getCurrent", "()C");
mw.visitInsn(DUP);
mw.visitVarInsn(ISTORE, context.var("ch"));
mw.visitVarInsn(BIPUSH, ',');
mw.visitJumpInsn(IF_ICMPNE, quickElseIf_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "next", "()C");
mw.visitInsn(POP);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.COMMA);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "setToken", "(I)V");
mw.visitJumpInsn(GOTO, quickEnd_);
mw.visitLabel(quickElseIf_);
mw.visitVarInsn(ILOAD, context.var("ch"));
mw.visitVarInsn(BIPUSH, ']');
mw.visitJumpInsn(IF_ICMPNE, quickElseIfEOI_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "next", "()C");
mw.visitInsn(POP);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.RBRACKET);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "setToken", "(I)V");
mw.visitJumpInsn(GOTO, quickEnd_);
mw.visitLabel(quickElseIfEOI_);
mw.visitVarInsn(ILOAD, context.var("ch"));
mw.visitVarInsn(BIPUSH, (char) JSONLexer.EOI);
mw.visitJumpInsn(IF_ICMPNE, quickElse_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "next", "()C");
mw.visitInsn(POP);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.EOF);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "setToken", "(I)V");
mw.visitJumpInsn(GOTO, quickEnd_);
mw.visitLabel(quickElse_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.COMMA);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "nextToken", "(I)V");
mw.visitLabel(quickEnd_);
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitInsn(ARETURN);
mw.visitMaxs(5, context.variantIndex);
mw.visitEnd();
}
private void _deserialze(ClassWriter cw, Context context) {
if (context.fieldInfoList.length == 0) {
return;
}
for (FieldInfo fieldInfo : context.fieldInfoList) {
Class> fieldClass = fieldInfo.fieldClass;
Type fieldType = fieldInfo.fieldType;
if (fieldClass == char.class) {
return;
}
if (Collection.class.isAssignableFrom(fieldClass)) {
if (fieldType instanceof ParameterizedType) {
Type itemType = ((ParameterizedType) fieldType).getActualTypeArguments()[0];
if (itemType instanceof Class) {
continue;
} else {
return;
}
} else {
return;
}
}
}
JavaBeanInfo beanInfo = context.beanInfo;
context.fieldInfoList = beanInfo.sortedFields;
MethodVisitor mw = new MethodWriter(cw, ACC_PUBLIC, "deserialze",
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;I)Ljava/lang/Object;",
null, null);
Label reset_ = new Label();
Label super_ = new Label();
Label return_ = new Label();
Label end_ = new Label();
defineVarLexer(context, mw);
{
Label next_ = new Label();
// isSupportArrayToBean
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "token", "()I");
mw.visitLdcInsn(JSONToken.LBRACKET);
mw.visitJumpInsn(IF_ICMPNE, next_);
if ((beanInfo.parserFeatures & Feature.SupportArrayToBean.mask) == 0) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ILOAD, 4);
mw.visitLdcInsn(Feature.SupportArrayToBean.mask);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "isEnabled", "(II)Z");
mw.visitJumpInsn(IFEQ, next_);
}
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, Context.parser);
mw.visitVarInsn(ALOAD, 2);
mw.visitVarInsn(ALOAD, 3);
mw.visitInsn(ACONST_NULL); //mw.visitVarInsn(ALOAD, 5);
mw.visitMethodInsn(INVOKESPECIAL, //
context.className, //
"deserialzeArrayMapping", //
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
mw.visitInsn(ARETURN);
mw.visitLabel(next_);
// deserialzeArrayMapping
}
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(Feature.SortFeidFastMatch.mask);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "isEnabled", "(I)Z");
Label continue_ = new Label();
mw.visitJumpInsn(IFNE, continue_);
mw.visitJumpInsn(GOTO_W, super_);
mw.visitLabel(continue_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(context.clazz.getName());
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanType", "(Ljava/lang/String;)I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.NOT_MATCH);
Label continue_2 = new Label();
mw.visitJumpInsn(IF_ICMPNE, continue_2);
mw.visitJumpInsn(GOTO_W, super_);
mw.visitLabel(continue_2);
mw.visitVarInsn(ALOAD, 1); // parser
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getContext", "()" + desc(ParseContext.class));
mw.visitVarInsn(ASTORE, context.var("mark_context"));
// ParseContext context = parser.getContext();
mw.visitInsn(ICONST_0);
mw.visitVarInsn(ISTORE, context.var("matchedCount"));
_createInstance(context, mw);
{
mw.visitVarInsn(ALOAD, 1); // parser
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getContext", "()" + desc(ParseContext.class));
mw.visitVarInsn(ASTORE, context.var("context"));
mw.visitVarInsn(ALOAD, 1); // parser
mw.visitVarInsn(ALOAD, context.var("context"));
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitVarInsn(ALOAD, 3); // fieldName
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "setContext", //
"(" + desc(ParseContext.class) + "Ljava/lang/Object;Ljava/lang/Object;)"
+ desc(ParseContext.class));
mw.visitVarInsn(ASTORE, context.var("childContext"));
}
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.END);
//mw.visitJumpInsn(IF_ICMPEQ, return_);
Label continue_3 = new Label();
mw.visitJumpInsn(IF_ICMPNE, continue_3);
mw.visitJumpInsn(GOTO_W, return_);
mw.visitLabel(continue_3);
mw.visitInsn(ICONST_0); // UNKOWN
mw.visitIntInsn(ISTORE, context.var("matchStat"));
int fieldListSize = context.fieldInfoList.length;
for (int i = 0; i < fieldListSize; i += 32) {
mw.visitInsn(ICONST_0);
mw.visitVarInsn(ISTORE, context.var("_asm_flag_" + (i / 32)));
}
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(Feature.InitStringFieldAsEmpty.mask);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "isEnabled", "(I)Z");
mw.visitIntInsn(ISTORE, context.var("initStringFieldAsEmpty"));
// declare and init
for (int i = 0; i < fieldListSize; ++i) {
FieldInfo fieldInfo = context.fieldInfoList[i];
Class> fieldClass = fieldInfo.fieldClass;
if (fieldClass == boolean.class //
|| fieldClass == byte.class //
|| fieldClass == short.class //
|| fieldClass == int.class) {
mw.visitInsn(ICONST_0);
mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == long.class) {
mw.visitInsn(LCONST_0);
mw.visitVarInsn(LSTORE, context.var_asm(fieldInfo, 2));
} else if (fieldClass == float.class) {
mw.visitInsn(FCONST_0);
mw.visitVarInsn(FSTORE, context.var_asm(fieldInfo));
} else if (fieldClass == double.class) {
mw.visitInsn(DCONST_0);
mw.visitVarInsn(DSTORE, context.var_asm(fieldInfo, 2));
} else {
if (fieldClass == String.class) {
Label flagEnd_ = new Label();
Label flagElse_ = new Label();
mw.visitVarInsn(ILOAD, context.var("initStringFieldAsEmpty"));
mw.visitJumpInsn(IFEQ, flagElse_);
_setFlag(mw, context, i);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "stringDefaultValue", "()Ljava/lang/String;");
mw.visitJumpInsn(GOTO, flagEnd_);
mw.visitLabel(flagElse_);
mw.visitInsn(ACONST_NULL);
mw.visitLabel(flagEnd_);
} else {
mw.visitInsn(ACONST_NULL);
}
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
}
}
for (int i = 0; i < fieldListSize; ++i) {
FieldInfo fieldInfo = context.fieldInfoList[i];
Class> fieldClass = fieldInfo.fieldClass;
Type fieldType = fieldInfo.fieldType;
Label notMatch_ = new Label();
if (fieldClass == boolean.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldBoolean", "([C)Z");
mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == byte.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Byte.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == short.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Short.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == int.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
mw.visitVarInsn(ISTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Integer.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldInt", "([C)I");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == long.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldLong", "([C)J");
mw.visitVarInsn(LSTORE, context.var_asm(fieldInfo, 2));
} else if (fieldClass == Long.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldLong", "([C)J");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == float.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldFloat", "([C)F");
mw.visitVarInsn(FSTORE, context.var_asm(fieldInfo));
} else if (fieldClass == Float.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldFloat", "([C)F");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == double.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldDouble", "([C)D");
mw.visitVarInsn(DSTORE, context.var_asm(fieldInfo, 2));
} else if (fieldClass == Double.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldDouble", "([C)D");
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
Label valueNullEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.VALUE_NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNullEnd_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(valueNullEnd_);
} else if (fieldClass == String.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldString", "([C)Ljava/lang/String;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == java.util.Date.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldDate", "([C)Ljava/util/Date;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == java.util.UUID.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldUUID", "([C)Ljava/util/UUID;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == BigDecimal.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldDecimal", "([C)Ljava/math/BigDecimal;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == BigInteger.class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldBigInteger", "([C)Ljava/math/BigInteger;");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == int[].class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldIntArray", "([C)[I");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == float[].class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldFloatArray", "([C)[F");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass == float[][].class) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldFloatArray2", "([C)[[F");
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else if (fieldClass.isEnum()) {
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
_getFieldDeser(context, mw, fieldInfo);
mw.visitMethodInsn(INVOKEVIRTUAL, type(JavaBeanDeserializer.class), "scanEnum"
, "(L" + JSONLexerBase + ";[C" + desc(ObjectDeserializer.class) + ")Ljava/lang/Enum;");
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
// } else if (fieldClass.isEnum()) {
// mw.visitVarInsn(ALOAD, context.var("lexer"));
// mw.visitVarInsn(ALOAD, 0);
// mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
// Label enumNull_ = new Label();
// mw.visitInsn(ACONST_NULL);
// mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
// mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
//
// mw.visitVarInsn(ALOAD, 1);
//
// mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getSymbolTable", "()" + desc(SymbolTable.class));
//
// mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldSymbol",
// "([C" + desc(SymbolTable.class) + ")Ljava/lang/String;");
// mw.visitInsn(DUP);
// mw.visitVarInsn(ASTORE, context.var(fieldInfo.name + "_asm_enumName"));
//
// mw.visitJumpInsn(IFNULL, enumNull_);
//
// mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm_enumName"));
// mw.visitMethodInsn(INVOKEVIRTUAL, type(String.class), "length", "()I");
// mw.visitJumpInsn(IFEQ, enumNull_);
//
// mw.visitVarInsn(ALOAD, context.var(fieldInfo.name + "_asm_enumName"));
// mw.visitMethodInsn(INVOKESTATIC, type(fieldClass), "valueOf",
// "(Ljava/lang/String;)" + desc(fieldClass));
// mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
// mw.visitLabel(enumNull_);
} else if (Collection.class.isAssignableFrom(fieldClass)) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
Class> itemClass = TypeUtils.getCollectionItemClass(fieldType);
if (itemClass == String.class) {
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(fieldClass))); // cast
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "scanFieldStringArray",
"([CLjava/lang/Class;)" + desc(Collection.class));
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
} else {
_deserialze_list_obj(context, mw, reset_, fieldInfo, fieldClass, itemClass, i);
if (i == fieldListSize - 1) {
_deserialize_endCheck(context, mw, reset_);
}
continue;
}
} else {
_deserialze_obj(context, mw, reset_, fieldInfo, fieldClass, i);
if (i == fieldListSize - 1) {
_deserialize_endCheck(context, mw, reset_);
}
continue;
}
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
Label flag_ = new Label();
// mw.visitInsn(DUP);
mw.visitJumpInsn(IFLE, flag_);
_setFlag(mw, context, i);
mw.visitLabel(flag_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitInsn(DUP);
mw.visitVarInsn(ISTORE, context.var("matchStat"));
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.NOT_MATCH);
mw.visitJumpInsn(IF_ICMPEQ, reset_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitJumpInsn(IFLE, notMatch_);
// increment matchedCount
mw.visitVarInsn(ILOAD, context.var("matchedCount"));
mw.visitInsn(ICONST_1);
mw.visitInsn(IADD);
mw.visitVarInsn(ISTORE, context.var("matchedCount"));
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.END);
mw.visitJumpInsn(IF_ICMPEQ, end_);
mw.visitLabel(notMatch_);
if (i == fieldListSize - 1) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitFieldInsn(GETFIELD, JSONLexerBase, "matchStat", "I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.JSONLexerBase.END);
mw.visitJumpInsn(IF_ICMPNE, reset_);
}
} // endFor
mw.visitLabel(end_);
if (!context.clazz.isInterface() && !Modifier.isAbstract(context.clazz.getModifiers())) {
_batchSet(context, mw);
}
mw.visitLabel(return_);
_setContext(context, mw);
mw.visitVarInsn(ALOAD, context.var("instance"));
Method buildMethod = context.beanInfo.buildMethod;
if (buildMethod != null) {
mw.visitMethodInsn(INVOKEVIRTUAL, type(context.getInstClass()), buildMethod.getName(),
"()" + desc(buildMethod.getReturnType()));
}
mw.visitInsn(ARETURN);
mw.visitLabel(reset_);
_batchSet(context, mw);
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, 1);
mw.visitVarInsn(ALOAD, 2);
mw.visitVarInsn(ALOAD, 3);
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitVarInsn(ILOAD, 4);
int flagSize = (fieldListSize / 32);
if (fieldListSize != 0 && (fieldListSize % 32) != 0) {
flagSize += 1;
}
if (flagSize == 1) {
mw.visitInsn(ICONST_1);
} else {
mw.visitIntInsn(BIPUSH, flagSize);
}
mw.visitIntInsn(NEWARRAY, T_INT);
for (int i = 0; i < flagSize; ++i) {
mw.visitInsn(DUP);
if (i == 0) {
mw.visitInsn(ICONST_0);
} else if (i == 1) {
mw.visitInsn(ICONST_1);
} else {
mw.visitIntInsn(BIPUSH, i);
}
mw.visitVarInsn(ILOAD, context.var("_asm_flag_" + i));
mw.visitInsn(IASTORE);
}
mw.visitMethodInsn(INVOKEVIRTUAL, type(JavaBeanDeserializer.class),
"parseRest", "(L" + DefaultJSONParser
+ ";Ljava/lang/reflect/Type;Ljava/lang/Object;Ljava/lang/Object;I[I)Ljava/lang/Object;");
mw.visitTypeInsn(CHECKCAST, type(context.clazz)); // cast
mw.visitInsn(ARETURN);
mw.visitLabel(super_);
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, 1);
mw.visitVarInsn(ALOAD, 2);
mw.visitVarInsn(ALOAD, 3);
mw.visitVarInsn(ILOAD, 4);
mw.visitMethodInsn(INVOKESPECIAL, type(JavaBeanDeserializer.class), //
"deserialze", //
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;I)Ljava/lang/Object;");
mw.visitInsn(ARETURN);
mw.visitMaxs(10, context.variantIndex);
mw.visitEnd();
}
private void defineVarLexer(Context context, MethodVisitor mw) {
mw.visitVarInsn(ALOAD, 1);
mw.visitFieldInsn(GETFIELD, DefaultJSONParser, "lexer", desc(JSONLexer.class));
mw.visitTypeInsn(CHECKCAST, JSONLexerBase); // cast
mw.visitVarInsn(ASTORE, context.var("lexer"));
}
private void _createInstance(Context context, MethodVisitor mw) {
JavaBeanInfo beanInfo = context.beanInfo;
Constructor> defaultConstructor = beanInfo.defaultConstructor;
if (Modifier.isPublic(defaultConstructor.getModifiers())) {
mw.visitTypeInsn(NEW, type(context.getInstClass()));
mw.visitInsn(DUP);
mw.visitMethodInsn(INVOKESPECIAL, type(defaultConstructor.getDeclaringClass()), "", "()V");
} else {
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, 1);
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, type(JavaBeanDeserializer.class), "clazz", "Ljava/lang/Class;");
mw.visitMethodInsn(INVOKESPECIAL, type(JavaBeanDeserializer.class), "createInstance",
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;)Ljava/lang/Object;");
mw.visitTypeInsn(CHECKCAST, type(context.getInstClass())); // cast
}
mw.visitVarInsn(ASTORE, context.var("instance"));
}
private void _batchSet(Context context, MethodVisitor mw) {
_batchSet(context, mw, true);
}
private void _batchSet(Context context, MethodVisitor mw, boolean flag) {
for (int i = 0, size = context.fieldInfoList.length; i < size; ++i) {
Label notSet_ = new Label();
if (flag) {
_isFlag(mw, context, i, notSet_);
}
FieldInfo fieldInfo = context.fieldInfoList[i];
_loadAndSet(context, mw, fieldInfo);
if (flag) {
mw.visitLabel(notSet_);
}
}
}
private void _loadAndSet(Context context, MethodVisitor mw, FieldInfo fieldInfo) {
Class> fieldClass = fieldInfo.fieldClass;
Type fieldType = fieldInfo.fieldType;
if (fieldClass == boolean.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitVarInsn(ILOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
} else if (fieldClass == byte.class //
|| fieldClass == short.class //
|| fieldClass == int.class //
|| fieldClass == char.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitVarInsn(ILOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
} else if (fieldClass == long.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitVarInsn(LLOAD, context.var_asm(fieldInfo, 2));
if (fieldInfo.method != null) {
mw.visitMethodInsn(INVOKEVIRTUAL, type(context.getInstClass()), fieldInfo.method.getName(),
desc(fieldInfo.method));
if (!fieldInfo.method.getReturnType().equals(Void.TYPE)) {
mw.visitInsn(POP);
}
} else {
mw.visitFieldInsn(PUTFIELD, type(fieldInfo.declaringClass), fieldInfo.field.getName(),
desc(fieldInfo.fieldClass));
}
} else if (fieldClass == float.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitVarInsn(FLOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
} else if (fieldClass == double.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitVarInsn(DLOAD, context.var_asm(fieldInfo, 2));
_set(context, mw, fieldInfo);
} else if (fieldClass == String.class) {
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
} else if (fieldClass.isEnum()) {
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
} else if (Collection.class.isAssignableFrom(fieldClass)) {
mw.visitVarInsn(ALOAD, context.var("instance"));
Type itemType = TypeUtils.getCollectionItemClass(fieldType);
if (itemType == String.class) {
mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
} else {
mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
}
_set(context, mw, fieldInfo);
} else {
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
_set(context, mw, fieldInfo);
}
}
private void _set(Context context, MethodVisitor mw, FieldInfo fieldInfo) {
Method method = fieldInfo.method;
if (method != null) {
Class> declaringClass = method.getDeclaringClass();
mw.visitMethodInsn(declaringClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, type(fieldInfo.declaringClass), method.getName(), desc(method));
if (!fieldInfo.method.getReturnType().equals(Void.TYPE)) {
mw.visitInsn(POP);
}
} else {
mw.visitFieldInsn(PUTFIELD, type(fieldInfo.declaringClass), fieldInfo.field.getName(),
desc(fieldInfo.fieldClass));
}
}
private void _setContext(Context context, MethodVisitor mw) {
mw.visitVarInsn(ALOAD, 1); // parser
mw.visitVarInsn(ALOAD, context.var("context"));
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "setContext", "(" + desc(ParseContext.class) + ")V");
Label endIf_ = new Label();
mw.visitVarInsn(ALOAD, context.var("childContext"));
mw.visitJumpInsn(IFNULL, endIf_);
mw.visitVarInsn(ALOAD, context.var("childContext"));
mw.visitVarInsn(ALOAD, context.var("instance"));
mw.visitFieldInsn(PUTFIELD, type(ParseContext.class), "object", "Ljava/lang/Object;");
mw.visitLabel(endIf_);
}
private void _deserialize_endCheck(Context context, MethodVisitor mw, Label reset_) {
mw.visitIntInsn(ILOAD, context.var("matchedCount"));
mw.visitJumpInsn(IFLE, reset_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "token", "()I");
mw.visitLdcInsn(JSONToken.RBRACE);
mw.visitJumpInsn(IF_ICMPNE, reset_);
// mw.visitLabel(nextToken_);
_quickNextTokenComma(context, mw);
}
private void _deserialze_list_obj(Context context, MethodVisitor mw, Label reset_, FieldInfo fieldInfo,
Class> fieldClass, Class> itemType, int i) {
Label _end_if = new Label();
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "matchField", "([C)Z");
mw.visitJumpInsn(IFEQ, _end_if);
_setFlag(mw, context, i);
Label valueNotNull_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "token", "()I");
mw.visitLdcInsn(JSONToken.NULL);
mw.visitJumpInsn(IF_ICMPNE, valueNotNull_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.COMMA);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "nextToken", "(I)V");
mw.visitJumpInsn(GOTO, _end_if);
// loop_end_
mw.visitLabel(valueNotNull_);
Label storeCollection_ = new Label(), endSet_ = new Label(), lbacketNormal_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "token", "()I");
mw.visitLdcInsn(JSONToken.SET);
mw.visitJumpInsn(IF_ICMPNE, endSet_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.LBRACKET);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "nextToken", "(I)V");
_newCollection(mw, fieldClass, i, true);
mw.visitJumpInsn(GOTO, storeCollection_);
mw.visitLabel(endSet_);
// if (lexer.token() != JSONToken.LBRACKET) reset
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "token", "()I");
mw.visitLdcInsn(JSONToken.LBRACKET);
mw.visitJumpInsn(IF_ICMPEQ, lbacketNormal_);
// if (lexer.token() == JSONToken.LBRACE) reset
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "token", "()I");
mw.visitLdcInsn(JSONToken.LBRACE);
mw.visitJumpInsn(IF_ICMPNE, reset_);
_newCollection(mw, fieldClass, i, false);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
_getCollectionFieldItemDeser(context, mw, fieldInfo, itemType);
mw.visitVarInsn(ALOAD, 1);
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(itemType)));
mw.visitInsn(ICONST_0);
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
mw.visitMethodInsn(INVOKEINTERFACE, type(ObjectDeserializer.class), "deserialze",
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;)Ljava/lang/Object;");
mw.visitVarInsn(ASTORE, context.var("list_item_value"));
mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitVarInsn(ALOAD, context.var("list_item_value"));
if (fieldClass.isInterface()) {
mw.visitMethodInsn(INVOKEINTERFACE, type(fieldClass), "add", "(Ljava/lang/Object;)Z");
} else {
mw.visitMethodInsn(INVOKEVIRTUAL, type(fieldClass), "add", "(Ljava/lang/Object;)Z");
}
mw.visitInsn(POP);
mw.visitJumpInsn(GOTO, _end_if);
mw.visitLabel(lbacketNormal_);
_newCollection(mw, fieldClass, i, false);
mw.visitLabel(storeCollection_);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
boolean isPrimitive = ParserConfig.isPrimitive2(fieldInfo.fieldClass);
_getCollectionFieldItemDeser(context, mw, fieldInfo, itemType);
if (isPrimitive) {
mw.visitMethodInsn(INVOKEINTERFACE, type(ObjectDeserializer.class), "getFastMatchToken", "()I");
mw.visitVarInsn(ISTORE, context.var("fastMatchToken"));
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ILOAD, context.var("fastMatchToken"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "nextToken", "(I)V");
} else {
mw.visitInsn(POP);
mw.visitLdcInsn(JSONToken.LBRACE);
mw.visitVarInsn(ISTORE, context.var("fastMatchToken"));
_quickNextToken(context, mw, JSONToken.LBRACE);
}
{ // setContext
mw.visitVarInsn(ALOAD, 1);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getContext", "()" + desc(ParseContext.class));
mw.visitVarInsn(ASTORE, context.var("listContext"));
mw.visitVarInsn(ALOAD, 1); // parser
mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitLdcInsn(fieldInfo.name);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "setContext",
"(Ljava/lang/Object;Ljava/lang/Object;)" + desc(ParseContext.class));
mw.visitInsn(POP);
}
Label loop_ = new Label();
Label loop_end_ = new Label();
// for (;;) {
mw.visitInsn(ICONST_0);
mw.visitVarInsn(ISTORE, context.var("i"));
mw.visitLabel(loop_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "token", "()I");
mw.visitLdcInsn(JSONToken.RBRACKET);
mw.visitJumpInsn(IF_ICMPEQ, loop_end_);
// Object value = itemDeserializer.deserialze(parser, null);
// array.add(value);
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_list_item_deser__",
desc(ObjectDeserializer.class));
mw.visitVarInsn(ALOAD, 1);
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(itemType)));
mw.visitVarInsn(ILOAD, context.var("i"));
mw.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
mw.visitMethodInsn(INVOKEINTERFACE, type(ObjectDeserializer.class), "deserialze",
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;)Ljava/lang/Object;");
mw.visitVarInsn(ASTORE, context.var("list_item_value"));
mw.visitIincInsn(context.var("i"), 1);
mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitVarInsn(ALOAD, context.var("list_item_value"));
if (fieldClass.isInterface()) {
mw.visitMethodInsn(INVOKEINTERFACE, type(fieldClass), "add", "(Ljava/lang/Object;)Z");
} else {
mw.visitMethodInsn(INVOKEVIRTUAL, type(fieldClass), "add", "(Ljava/lang/Object;)Z");
}
mw.visitInsn(POP);
mw.visitVarInsn(ALOAD, 1);
mw.visitVarInsn(ALOAD, context.var_asm(fieldInfo));
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "checkListResolve", "(Ljava/util/Collection;)V");
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "token", "()I");
mw.visitLdcInsn(JSONToken.COMMA);
mw.visitJumpInsn(IF_ICMPNE, loop_);
if (isPrimitive) {
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ILOAD, context.var("fastMatchToken"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "nextToken", "(I)V");
} else {
_quickNextToken(context, mw, JSONToken.LBRACE);
}
mw.visitJumpInsn(GOTO, loop_);
mw.visitLabel(loop_end_);
// mw.visitVarInsn(ASTORE, context.var("context"));
// parser.setContext(context);
{ // setContext
mw.visitVarInsn(ALOAD, 1); // parser
mw.visitVarInsn(ALOAD, context.var("listContext"));
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "setContext", "(" + desc(ParseContext.class) + ")V");
}
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "token", "()I");
mw.visitLdcInsn(JSONToken.RBRACKET);
mw.visitJumpInsn(IF_ICMPNE, reset_);
_quickNextTokenComma(context, mw);
// lexer.nextToken(JSONToken.COMMA);
mw.visitLabel(_end_if);
}
private void _quickNextToken(Context context, MethodVisitor mw, int token) {
Label quickElse_ = new Label(), quickEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "getCurrent", "()C");
if (token == JSONToken.LBRACE) {
mw.visitVarInsn(BIPUSH, '{');
} else if (token == JSONToken.LBRACKET) {
mw.visitVarInsn(BIPUSH, '[');
} else {
throw new IllegalStateException();
}
mw.visitJumpInsn(IF_ICMPNE, quickElse_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "next", "()C");
mw.visitInsn(POP);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(token);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "setToken", "(I)V");
mw.visitJumpInsn(GOTO, quickEnd_);
mw.visitLabel(quickElse_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(token);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "nextToken", "(I)V");
mw.visitLabel(quickEnd_);
}
private void _quickNextTokenComma(Context context, MethodVisitor mw) {
Label quickElse_ = new Label(), quickElseIf0_ = new Label(), quickElseIf1_ = new Label(), quickElseIf2_ = new Label(), quickEnd_ = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "getCurrent", "()C");
mw.visitInsn(DUP);
mw.visitVarInsn(ISTORE, context.var("ch"));
mw.visitVarInsn(BIPUSH, ',');
mw.visitJumpInsn(IF_ICMPNE, quickElseIf0_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "next", "()C");
mw.visitInsn(POP);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.COMMA);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "setToken", "(I)V");
mw.visitJumpInsn(GOTO, quickEnd_);
mw.visitLabel(quickElseIf0_);
mw.visitVarInsn(ILOAD, context.var("ch"));
mw.visitVarInsn(BIPUSH, '}');
mw.visitJumpInsn(IF_ICMPNE, quickElseIf1_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "next", "()C");
mw.visitInsn(POP);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.RBRACE);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "setToken", "(I)V");
mw.visitJumpInsn(GOTO, quickEnd_);
mw.visitLabel(quickElseIf1_);
mw.visitVarInsn(ILOAD, context.var("ch"));
mw.visitVarInsn(BIPUSH, ']');
mw.visitJumpInsn(IF_ICMPNE, quickElseIf2_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "next", "()C");
mw.visitInsn(POP);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.RBRACKET);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "setToken", "(I)V");
mw.visitJumpInsn(GOTO, quickEnd_);
mw.visitLabel(quickElseIf2_);
mw.visitVarInsn(ILOAD, context.var("ch"));
mw.visitVarInsn(BIPUSH, JSONLexer.EOI);
mw.visitJumpInsn(IF_ICMPNE, quickElse_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitLdcInsn(JSONToken.EOF);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "setToken", "(I)V");
mw.visitJumpInsn(GOTO, quickEnd_);
mw.visitLabel(quickElse_);
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "nextToken", "()V");
mw.visitLabel(quickEnd_);
}
private void _getCollectionFieldItemDeser(Context context, MethodVisitor mw, FieldInfo fieldInfo,
Class> itemType) {
Label notNull_ = new Label();
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_list_item_deser__",
desc(ObjectDeserializer.class));
mw.visitJumpInsn(IFNONNULL, notNull_);
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, 1);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getConfig", "()" + desc(ParserConfig.class));
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(itemType)));
mw.visitMethodInsn(INVOKEVIRTUAL, type(ParserConfig.class), "getDeserializer",
"(Ljava/lang/reflect/Type;)" + desc(ObjectDeserializer.class));
mw.visitFieldInsn(PUTFIELD, context.className, fieldInfo.name + "_asm_list_item_deser__",
desc(ObjectDeserializer.class));
mw.visitLabel(notNull_);
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, fieldInfo.name + "_asm_list_item_deser__",
desc(ObjectDeserializer.class));
}
private void _newCollection(MethodVisitor mw, Class> fieldClass, int i, boolean set) {
if (fieldClass.isAssignableFrom(ArrayList.class) && !set) {
mw.visitTypeInsn(NEW, "java/util/ArrayList");
mw.visitInsn(DUP);
mw.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "", "()V");
} else if (fieldClass.isAssignableFrom(LinkedList.class) && !set) {
mw.visitTypeInsn(NEW, type(LinkedList.class));
mw.visitInsn(DUP);
mw.visitMethodInsn(INVOKESPECIAL, type(LinkedList.class), "", "()V");
} else if (fieldClass.isAssignableFrom(HashSet.class)) {
mw.visitTypeInsn(NEW, type(HashSet.class));
mw.visitInsn(DUP);
mw.visitMethodInsn(INVOKESPECIAL, type(HashSet.class), "", "()V");
} else if (fieldClass.isAssignableFrom(TreeSet.class)) {
mw.visitTypeInsn(NEW, type(TreeSet.class));
mw.visitInsn(DUP);
mw.visitMethodInsn(INVOKESPECIAL, type(TreeSet.class), "", "()V");
} else if (fieldClass.isAssignableFrom(LinkedHashSet.class)) {
mw.visitTypeInsn(NEW, type(LinkedHashSet.class));
mw.visitInsn(DUP);
mw.visitMethodInsn(INVOKESPECIAL, type(LinkedHashSet.class), "", "()V");
} else if (set) {
mw.visitTypeInsn(NEW, type(HashSet.class));
mw.visitInsn(DUP);
mw.visitMethodInsn(INVOKESPECIAL, type(HashSet.class), "", "()V");
} else {
mw.visitVarInsn(ALOAD, 0);
mw.visitLdcInsn(i);
mw.visitMethodInsn(INVOKEVIRTUAL, type(JavaBeanDeserializer.class), "getFieldType",
"(I)Ljava/lang/reflect/Type;");
mw.visitMethodInsn(INVOKESTATIC, type(TypeUtils.class), "createCollection",
"(Ljava/lang/reflect/Type;)Ljava/util/Collection;");
}
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
}
private void _deserialze_obj(Context context, MethodVisitor mw, Label reset_, FieldInfo fieldInfo,
Class> fieldClass, int i) {
Label matched_ = new Label();
Label _end_if = new Label();
mw.visitVarInsn(ALOAD, context.var("lexer"));
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldName(fieldInfo), "[C");
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase, "matchField", "([C)Z");
mw.visitJumpInsn(IFNE, matched_);
mw.visitInsn(ACONST_NULL);
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitJumpInsn(GOTO, _end_if);
mw.visitLabel(matched_);
_setFlag(mw, context, i);
// increment matchedCount
mw.visitVarInsn(ILOAD, context.var("matchedCount"));
mw.visitInsn(ICONST_1);
mw.visitInsn(IADD);
mw.visitVarInsn(ISTORE, context.var("matchedCount"));
_deserObject(context, mw, fieldInfo, fieldClass, i);
mw.visitVarInsn(ALOAD, 1);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getResolveStatus", "()I");
mw.visitLdcInsn(com.alibaba.fastjson.parser.DefaultJSONParser.NeedToResolve);
mw.visitJumpInsn(IF_ICMPNE, _end_if);
mw.visitVarInsn(ALOAD, 1);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getLastResolveTask", "()" + desc(ResolveTask.class));
mw.visitVarInsn(ASTORE, context.var("resolveTask"));
mw.visitVarInsn(ALOAD, context.var("resolveTask"));
mw.visitVarInsn(ALOAD, 1);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getContext", "()" + desc(ParseContext.class));
mw.visitFieldInsn(PUTFIELD, type(ResolveTask.class), "ownerContext", desc(ParseContext.class));
mw.visitVarInsn(ALOAD, context.var("resolveTask"));
mw.visitVarInsn(ALOAD, 0);
mw.visitLdcInsn(fieldInfo.name);
mw.visitMethodInsn(INVOKEVIRTUAL, type(JavaBeanDeserializer.class), "getFieldDeserializer",
"(Ljava/lang/String;)" + desc(FieldDeserializer.class));
mw.visitFieldInsn(PUTFIELD, type(ResolveTask.class), "fieldDeserializer", desc(FieldDeserializer.class));
mw.visitVarInsn(ALOAD, 1);
mw.visitLdcInsn(com.alibaba.fastjson.parser.DefaultJSONParser.NONE);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "setResolveStatus", "(I)V");
mw.visitLabel(_end_if);
}
private void _deserObject(Context context, MethodVisitor mw, FieldInfo fieldInfo, Class> fieldClass, int i) {
_getFieldDeser(context, mw, fieldInfo);
Label instanceOfElse_ = new Label(), instanceOfEnd_ = new Label();
if ((fieldInfo.parserFeatures & Feature.SupportArrayToBean.mask) != 0) {
mw.visitInsn(DUP);
mw.visitTypeInsn(INSTANCEOF, type(JavaBeanDeserializer.class));
mw.visitJumpInsn(IFEQ, instanceOfElse_);
mw.visitTypeInsn(CHECKCAST, type(JavaBeanDeserializer.class)); // cast
mw.visitVarInsn(ALOAD, 1);
if (fieldInfo.fieldType instanceof Class) {
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(fieldInfo.fieldClass)));
} else {
mw.visitVarInsn(ALOAD, 0);
mw.visitLdcInsn(i);
mw.visitMethodInsn(INVOKEVIRTUAL, type(JavaBeanDeserializer.class), "getFieldType",
"(I)Ljava/lang/reflect/Type;");
}
mw.visitLdcInsn(fieldInfo.name);
mw.visitLdcInsn(fieldInfo.parserFeatures);
mw.visitMethodInsn(INVOKEVIRTUAL, type(JavaBeanDeserializer.class), "deserialze",
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;I)Ljava/lang/Object;");
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitJumpInsn(GOTO, instanceOfEnd_);
mw.visitLabel(instanceOfElse_);
}
mw.visitVarInsn(ALOAD, 1);
if (fieldInfo.fieldType instanceof Class) {
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(fieldInfo.fieldClass)));
} else {
mw.visitVarInsn(ALOAD, 0);
mw.visitLdcInsn(i);
mw.visitMethodInsn(INVOKEVIRTUAL, type(JavaBeanDeserializer.class), "getFieldType",
"(I)Ljava/lang/reflect/Type;");
}
mw.visitLdcInsn(fieldInfo.name);
mw.visitMethodInsn(INVOKEINTERFACE, type(ObjectDeserializer.class), "deserialze",
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;Ljava/lang/Object;)Ljava/lang/Object;");
mw.visitTypeInsn(CHECKCAST, type(fieldClass)); // cast
mw.visitVarInsn(ASTORE, context.var_asm(fieldInfo));
mw.visitLabel(instanceOfEnd_);
}
private void _getFieldDeser(Context context, MethodVisitor mw, FieldInfo fieldInfo) {
Label notNull_ = new Label();
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldDeserName(fieldInfo), desc(ObjectDeserializer.class));
mw.visitJumpInsn(IFNONNULL, notNull_);
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, 1);
mw.visitMethodInsn(INVOKEVIRTUAL, DefaultJSONParser, "getConfig", "()" + desc(ParserConfig.class));
mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(fieldInfo.fieldClass)));
mw.visitMethodInsn(INVOKEVIRTUAL, type(ParserConfig.class), "getDeserializer",
"(Ljava/lang/reflect/Type;)" + desc(ObjectDeserializer.class));
mw.visitFieldInsn(PUTFIELD, context.className, context.fieldDeserName(fieldInfo), desc(ObjectDeserializer.class));
mw.visitLabel(notNull_);
mw.visitVarInsn(ALOAD, 0);
mw.visitFieldInsn(GETFIELD, context.className, context.fieldDeserName(fieldInfo), desc(ObjectDeserializer.class));
}
static class Context {
static final int parser = 1;
static final int type = 2;
static final int fieldName = 3;
private int variantIndex = -1;
private final Map variants = new HashMap();
private final Class> clazz;
private final JavaBeanInfo beanInfo;
private final String className;
private FieldInfo[] fieldInfoList;
public Context(String className, ParserConfig config, JavaBeanInfo beanInfo, int initVariantIndex){
this.className = className;
this.clazz = beanInfo.clazz;
this.variantIndex = initVariantIndex;
this.beanInfo = beanInfo;
fieldInfoList = beanInfo.fields;
}
public Class> getInstClass() {
Class> instClass = beanInfo.builderClass;
if (instClass == null) {
instClass = clazz;
}
return instClass;
}
public int var(String name, int increment) {
Integer i = variants.get(name);
if (i == null) {
variants.put(name, variantIndex);
variantIndex += increment;
}
i = variants.get(name);
return i.intValue();
}
public int var(String name) {
Integer i = variants.get(name);
if (i == null) {
variants.put(name, variantIndex++);
}
i = variants.get(name);
return i.intValue();
}
public int var_asm(FieldInfo fieldInfo) {
return var(fieldInfo.name + "_asm");
}
public int var_asm(FieldInfo fieldInfo, int increment) {
return var(fieldInfo.name + "_asm", increment);
}
public String fieldName(FieldInfo fieldInfo) {
return validIdent(fieldInfo.name)
? fieldInfo.name + "_asm_prefix__"
: "asm_field_" + TypeUtils.fnv1a_64_extract(fieldInfo.name);
}
public String fieldDeserName(FieldInfo fieldInfo) {
return validIdent(fieldInfo.name)
? fieldInfo.name + "_asm_deser__"
: "_asm_deser__" + TypeUtils.fnv1a_64_extract(fieldInfo.name);
}
boolean validIdent(String name) {
for (int i = 0; i < name.length(); ++i) {
char ch = name.charAt(i);
if (ch == 0) {
if (!IOUtils.firstIdentifier(ch)) {
return false;
}
} else {
if (!IOUtils.isIdent(ch)) {
return false;
}
}
}
return true;
}
}
private void _init(ClassWriter cw, Context context) {
for (int i = 0, size = context.fieldInfoList.length; i < size; ++i) {
FieldInfo fieldInfo = context.fieldInfoList[i];
FieldWriter fw = new FieldWriter(cw, ACC_PUBLIC, context.fieldName(fieldInfo), "[C");
fw.visitEnd();
}
for (int i = 0, size = context.fieldInfoList.length; i < size; ++i) {
FieldInfo fieldInfo = context.fieldInfoList[i];
Class> fieldClass = fieldInfo.fieldClass;
if (fieldClass.isPrimitive()) {
continue;
}
if (Collection.class.isAssignableFrom(fieldClass)) {
FieldWriter fw = new FieldWriter(cw, ACC_PUBLIC, fieldInfo.name + "_asm_list_item_deser__",
desc(ObjectDeserializer.class));
fw.visitEnd();
} else {
FieldWriter fw = new FieldWriter(cw, ACC_PUBLIC, context.fieldDeserName(fieldInfo),
desc(ObjectDeserializer.class));
fw.visitEnd();
}
}
MethodVisitor mw = new MethodWriter(cw, ACC_PUBLIC, "",
"(" + desc(ParserConfig.class) + desc(JavaBeanInfo.class) + ")V", null, null);
mw.visitVarInsn(ALOAD, 0);
mw.visitVarInsn(ALOAD, 1);
mw.visitVarInsn(ALOAD, 2);
mw.visitMethodInsn(INVOKESPECIAL, type(JavaBeanDeserializer.class), "",
"(" + desc(ParserConfig.class) + desc(JavaBeanInfo.class) + ")V");
// init fieldNamePrefix
for (int i = 0, size = context.fieldInfoList.length; i < size; ++i) {
FieldInfo fieldInfo = context.fieldInfoList[i];
mw.visitVarInsn(ALOAD, 0);
mw.visitLdcInsn("\"" + fieldInfo.name + "\":"); // public char[] toCharArray()
mw.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "toCharArray", "()[C");
mw.visitFieldInsn(PUTFIELD, context.className, context.fieldName(fieldInfo), "[C");
}
mw.visitInsn(RETURN);
mw.visitMaxs(4, 4);
mw.visitEnd();
}
private void _createInstance(ClassWriter cw, Context context) {
Constructor> defaultConstructor = context.beanInfo.defaultConstructor;
if (!Modifier.isPublic(defaultConstructor.getModifiers())) {
return;
}
MethodVisitor mw = new MethodWriter(cw, ACC_PUBLIC, "createInstance",
"(L" + DefaultJSONParser + ";Ljava/lang/reflect/Type;)Ljava/lang/Object;",
null, null);
mw.visitTypeInsn(NEW, type(context.getInstClass()));
mw.visitInsn(DUP);
mw.visitMethodInsn(INVOKESPECIAL, type(context.getInstClass()), "