Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
lib/bson/parser/serializer.js
### 代码内容：
'use strict';

var writeIEEE754 = require('../float_parser').writeIEEE754,
  Long = require('../long').Long,
  Map = require('../map'),
  Binary = require('../binary').Binary;

var normalizedFunctionString = require('./utils').normalizedFunctionString;

// try {
//   var _Buffer = Uint8Array;
// } catch (e) {
//   _Buffer = Buffer;
// }

var regexp = /\x00/; // eslint-disable-line no-control-regex
var ignoreKeys = ['$db', '$ref', '$id', '$clusterTime'];

// To ensure that 0.4 of node works correctly
var isDate = function isDate(d) {
  return typeof d === 'object' && Object.prototype.toString.call(d) === '[object Date]';
};

var isRegExp = function isRegExp(d) {
  return Object.prototype.toString.call(d) === '[object RegExp]';
};

var serializeString = function(buffer, key, value, index, isArray) {
  // Encode String type
  buffer[index++] = BSON.BSON_DATA_STRING;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes + 1;
  buffer[index - 1] = 0;
  // Write the string
  var size = buffer.write(value, index + 4, 'utf8');
  // Write the size of the string to buffer
  buffer[index + 3] = ((size + 1) >> 24) & 0xff;
  buffer[index + 2] = ((size + 1) >> 16) & 0xff;
  buffer[index + 1] = ((size + 1) >> 8) & 0xff;
  buffer[index] = (size + 1) & 0xff;
  // Update index
  index = index + 4 + size;
  // Write zero
  buffer[index++] = 0;
  return index;
};

var serializeNumber = function(buffer, key, value, index, isArray) {
  // We have an integer value
  if (Math.floor(value) === value && value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) {
    // If the value fits in 32 bits encode as int, if it fits in a double
    // encode it as a double, otherwise long
    if (value >= BSON.BSON_INT32_MIN && value <= BSON.BSON_INT32_MAX) {
      // Set int type 32 bits or less
      buffer[index++] = BSON.BSON_DATA_INT;
      // Number of written bytes
      var numberOfWrittenBytes = !isArray
        ? buffer.write(key, index, 'utf8')
        : buffer.write(key, index, 'ascii');
      // Encode the name
      index = index + numberOfWrittenBytes;
      buffer[index++] = 0;
      // Write the int value
      buffer[index++] = value & 0xff;
      buffer[index++] = (value >> 8) & 0xff;
      buffer[index++] = (value >> 16) & 0xff;
      buffer[index++] = (value >> 24) & 0xff;
    } else if (value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) {
      // Encode as double
      buffer[index++] = BSON.BSON_DATA_NUMBER;
      // Number of written bytes
      numberOfWrittenBytes = !isArray
        ? buffer.write(key, index, 'utf8')
        : buffer.write(key, index, 'ascii');
      // Encode the name
      index = index + numberOfWrittenBytes;
      buffer[index++] = 0;
      // Write float
      writeIEEE754(buffer, value, index, 'little', 52, 8);
      // Ajust index
      index = index + 8;
    } else {
      // Set long type
      buffer[index++] = BSON.BSON_DATA_LONG;
      // Number of written bytes
      numberOfWrittenBytes = !isArray
        ? buffer.write(key, index, 'utf8')
        : buffer.write(key, index, 'ascii');
      // Encode the name
      index = index + numberOfWrittenBytes;
      buffer[index++] = 0;
      var longVal = Long.fromNumber(value);
      var lowBits = longVal.getLowBits();
      var highBits = longVal.getHighBits();
      // Encode low bits
      buffer[index++] = lowBits & 0xff;
      buffer[index++] = (lowBits >> 8) & 0xff;
      buffer[index++] = (lowBits >> 16) & 0xff;
      buffer[index++] = (lowBits >> 24) & 0xff;
      // Encode high bits
      buffer[index++] = highBits & 0xff;
      buffer[index++] = (highBits >> 8) & 0xff;
      buffer[index++] = (highBits >> 16) & 0xff;
      buffer[index++] = (highBits >> 24) & 0xff;
    }
  } else {
    // Encode as double
    buffer[index++] = BSON.BSON_DATA_NUMBER;
    // Number of written bytes
    numberOfWrittenBytes = !isArray
      ? buffer.write(key, index, 'utf8')
      : buffer.write(key, index, 'ascii');
    // Encode the name
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    // Write float
    writeIEEE754(buffer, value, index, 'little', 52, 8);
    // Ajust index
    index = index + 8;
  }

  return index;
};

var serializeNull = function(buffer, key, value, index, isArray) {
  // Set long type
  buffer[index++] = BSON.BSON_DATA_NULL;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  return index;
};

var serializeBoolean = function(buffer, key, value, index, isArray) {
  // Write the type
  buffer[index++] = BSON.BSON_DATA_BOOLEAN;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  // Encode the boolean value
  buffer[index++] = value ? 1 : 0;
  return index;
};

var serializeDate = function(buffer, key, value, index, isArray) {
  // Write the type
  buffer[index++] = BSON.BSON_DATA_DATE;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;

  // Write the date
  var dateInMilis = Long.fromNumber(value.getTime());
  var lowBits = dateInMilis.getLowBits();
  var highBits = dateInMilis.getHighBits();
  // Encode low bits
  buffer[index++] = lowBits & 0xff;
  buffer[index++] = (lowBits >> 8) & 0xff;
  buffer[index++] = (lowBits >> 16) & 0xff;
  buffer[index++] = (lowBits >> 24) & 0xff;
  // Encode high bits
  buffer[index++] = highBits & 0xff;
  buffer[index++] = (highBits >> 8) & 0xff;
  buffer[index++] = (highBits >> 16) & 0xff;
  buffer[index++] = (highBits >> 24) & 0xff;
  return index;
};

var serializeRegExp = function(buffer, key, value, index, isArray) {
  // Write the type
  buffer[index++] = BSON.BSON_DATA_REGEXP;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  if (value.source && value.source.match(regexp) != null) {
    throw Error('value ' + value.source + ' must not contain null bytes');
  }
  // Adjust the index
  index = index + buffer.write(value.source, index, 'utf8');
  // Write zero
  buffer[index++] = 0x00;
  // Write the parameters
  if (value.global) buffer[index++] = 0x73; // s
  if (value.ignoreCase) buffer[index++] = 0x69; // i
  if (value.multiline) buffer[index++] = 0x6d; // m
  // Add ending zero
  buffer[index++] = 0x00;
  return index;
};

var serializeBSONRegExp = function(buffer, key, value, index, isArray) {
  // Write the type
  buffer[index++] = BSON.BSON_DATA_REGEXP;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;

  // Check the pattern for 0 bytes
  if (value.pattern.match(regexp) != null) {
    // The BSON spec doesn't allow keys with null bytes because keys are
    // null-terminated.
    throw Error('pattern ' + value.pattern + ' must not contain null bytes');
  }

  // Adjust the index
  index = index + buffer.write(value.pattern, index, 'utf8');
  // Write zero
  buffer[index++] = 0x00;
  // Write the options
  index =
    index +
    buffer.write(
      value.options
        .split('')
        .sort()
        .join(''),
      index,
      'utf8'
    );
  // Add ending zero
  buffer[index++] = 0x00;
  return index;
};

var serializeMinMax = function(buffer, key, value, index, isArray) {
  // Write the type of either min or max key
  if (value === null) {
    buffer[index++] = BSON.BSON_DATA_NULL;
  } else if (value._bsontype === 'MinKey') {
    buffer[index++] = BSON.BSON_DATA_MIN_KEY;
  } else {
    buffer[index++] = BSON.BSON_DATA_MAX_KEY;
  }

  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  return index;
};

var serializeObjectId = function(buffer, key, value, index, isArray) {
  // Write the type
  buffer[index++] = BSON.BSON_DATA_OID;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');

  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;

  // Write the objectId into the shared buffer
  if (typeof value.id === 'string') {
    buffer.write(value.id, index, 'binary');
  } else if (value.id && value.id.copy) {
    value.id.copy(buffer, index, 0, 12);
  } else {
    throw new Error('object [' + JSON.stringify(value) + '] is not a valid ObjectId');
  }

  // Ajust index
  return index + 12;
};

var serializeBuffer = function(buffer, key, value, index, isArray) {
  // Write the type
  buffer[index++] = BSON.BSON_DATA_BINARY;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  // Get size of the buffer (current write point)
  var size = value.length;
  // Write the size of the string to buffer
  buffer[index++] = size & 0xff;
  buffer[index++] = (size >> 8) & 0xff;
  buffer[index++] = (size >> 16) & 0xff;
  buffer[index++] = (size >> 24) & 0xff;
  // Write the default subtype
  buffer[index++] = BSON.BSON_BINARY_SUBTYPE_DEFAULT;
  // Copy the content form the binary field to the buffer
  value.copy(buffer, index, 0, size);
  // Adjust the index
  index = index + size;
  return index;
};

var serializeObject = function(
  buffer,
  key,
  value,
  index,
  checkKeys,
  depth,
  serializeFunctions,
  ignoreUndefined,
  isArray,
  path
) {
  for (var i = 0; i < path.length; i++) {
    if (path[i] === value) throw new Error('cyclic dependency detected');
  }

  // Push value to stack
  path.push(value);
  // Write the type
  buffer[index++] = Array.isArray(value) ? BSON.BSON_DATA_ARRAY : BSON.BSON_DATA_OBJECT;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  var endIndex = serializeInto(
    buffer,
    value,
    checkKeys,
    index,
    depth + 1,
    serializeFunctions,
    ignoreUndefined,
    path
  );
  // Pop stack
  path.pop();
  // Write size
  return endIndex;
};

var serializeDecimal128 = function(buffer, key, value, index, isArray) {
  buffer[index++] = BSON.BSON_DATA_DECIMAL128;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  // Write the data from the value
  value.bytes.copy(buffer, index, 0, 16);
  return index + 16;
};

var serializeLong = function(buffer, key, value, index, isArray) {
  // Write the type
  buffer[index++] = value._bsontype === 'Long' ? BSON.BSON_DATA_LONG : BSON.BSON_DATA_TIMESTAMP;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  // Write the date
  var lowBits = value.getLowBits();
  var highBits = value.getHighBits();
  // Encode low bits
  buffer[index++] = lowBits & 0xff;
  buffer[index++] = (lowBits >> 8) & 0xff;
  buffer[index++] = (lowBits >> 16) & 0xff;
  buffer[index++] = (lowBits >> 24) & 0xff;
  // Encode high bits
  buffer[index++] = highBits & 0xff;
  buffer[index++] = (highBits >> 8) & 0xff;
  buffer[index++] = (highBits >> 16) & 0xff;
  buffer[index++] = (highBits >> 24) & 0xff;
  return index;
};

var serializeInt32 = function(buffer, key, value, index, isArray) {
  // Set int type 32 bits or less
  buffer[index++] = BSON.BSON_DATA_INT;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  // Write the int value
  buffer[index++] = value & 0xff;
  buffer[index++] = (value >> 8) & 0xff;
  buffer[index++] = (value >> 16) & 0xff;
  buffer[index++] = (value >> 24) & 0xff;
  return index;
};

var serializeDouble = function(buffer, key, value, index, isArray) {
  // Encode as double
  buffer[index++] = BSON.BSON_DATA_NUMBER;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  // Write float
  writeIEEE754(buffer, value, index, 'little', 52, 8);
  // Ajust index
  index = index + 8;
  return index;
};

var serializeFunction = function(buffer, key, value, index, checkKeys, depth, isArray) {
  buffer[index++] = BSON.BSON_DATA_CODE;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  // Function string
  var functionString = normalizedFunctionString(value);

  // Write the string
  var size = buffer.write(functionString, index + 4, 'utf8') + 1;
  // Write the size of the string to buffer
  buffer[index] = size & 0xff;
  buffer[index + 1] = (size >> 8) & 0xff;
  buffer[index + 2] = (size >> 16) & 0xff;
  buffer[index + 3] = (size >> 24) & 0xff;
  // Update index
  index = index + 4 + size - 1;
  // Write zero
  buffer[index++] = 0;
  return index;
};

var serializeCode = function(
  buffer,
  key,
  value,
  index,
  checkKeys,
  depth,
  serializeFunctions,
  ignoreUndefined,
  isArray
) {
  if (value.scope && typeof value.scope === 'object') {
    // Write the type
    buffer[index++] = BSON.BSON_DATA_CODE_W_SCOPE;
    // Number of written bytes
    var numberOfWrittenBytes = !isArray
      ? buffer.write(key, index, 'utf8')
      : buffer.write(key, index, 'ascii');
    // Encode the name
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;

    // Starting index
    var startIndex = index;

    // Serialize the function
    // Get the function string
    var functionString = typeof value.code === 'string' ? value.code : value.code.toString();
    // Index adjustment
    index = index + 4;
    // Write string into buffer
    var codeSize = buffer.write(functionString, index + 4, 'utf8') + 1;
    // Write the size of the string to buffer
    buffer[index] = codeSize & 0xff;
    buffer[index + 1] = (codeSize >> 8) & 0xff;
    buffer[index + 2] = (codeSize >> 16) & 0xff;
    buffer[index + 3] = (codeSize >> 24) & 0xff;
    // Write end 0
    buffer[index + 4 + codeSize - 1] = 0;
    // Write the
    index = index + codeSize + 4;

    //
    // Serialize the scope value
    var endIndex = serializeInto(
      buffer,
      value.scope,
      checkKeys,
      index,
      depth + 1,
      serializeFunctions,
      ignoreUndefined
    );
    index = endIndex - 1;

    // Writ the total
    var totalSize = endIndex - startIndex;

    // Write the total size of the object
    buffer[startIndex++] = totalSize & 0xff;
    buffer[startIndex++] = (totalSize >> 8) & 0xff;
    buffer[startIndex++] = (totalSize >> 16) & 0xff;
    buffer[startIndex++] = (totalSize >> 24) & 0xff;
    // Write trailing zero
    buffer[index++] = 0;
  } else {
    buffer[index++] = BSON.BSON_DATA_CODE;
    // Number of written bytes
    numberOfWrittenBytes = !isArray
      ? buffer.write(key, index, 'utf8')
      : buffer.write(key, index, 'ascii');
    // Encode the name
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    // Function string
    functionString = value.code.toString();
    // Write the string
    var size = buffer.write(functionString, index + 4, 'utf8') + 1;
    // Write the size of the string to buffer
    buffer[index] = size & 0xff;
    buffer[index + 1] = (size >> 8) & 0xff;
    buffer[index + 2] = (size >> 16) & 0xff;
    buffer[index + 3] = (size >> 24) & 0xff;
    // Update index
    index = index + 4 + size - 1;
    // Write zero
    buffer[index++] = 0;
  }

  return index;
};

var serializeBinary = function(buffer, key, value, index, isArray) {
  // Write the type
  buffer[index++] = BSON.BSON_DATA_BINARY;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  // Extract the buffer
  var data = value.value(true);
  // Calculate size
  var size = value.position;
  // Add the deprecated 02 type 4 bytes of size to total
  if (value.sub_type === Binary.SUBTYPE_BYTE_ARRAY) size = size + 4;
  // Write the size of the string to buffer
  buffer[index++] = size & 0xff;
  buffer[index++] = (size >> 8) & 0xff;
  buffer[index++] = (size >> 16) & 0xff;
  buffer[index++] = (size >> 24) & 0xff;
  // Write the subtype to the buffer
  buffer[index++] = value.sub_type;

  // If we have binary type 2 the 4 first bytes are the size
  if (value.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
    size = size - 4;
    buffer[index++] = size & 0xff;
    buffer[index++] = (size >> 8) & 0xff;
    buffer[index++] = (size >> 16) & 0xff;
    buffer[index++] = (size >> 24) & 0xff;
  }

  // Write the data to the object
  data.copy(buffer, index, 0, value.position);
  // Adjust the index
  index = index + value.position;
  return index;
};

var serializeSymbol = function(buffer, key, value, index, isArray) {
  // Write the type
  buffer[index++] = BSON.BSON_DATA_SYMBOL;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');
  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;
  // Write the string
  var size = buffer.write(value.value, index + 4, 'utf8') + 1;
  // Write the size of the string to buffer
  buffer[index] = size & 0xff;
  buffer[index + 1] = (size >> 8) & 0xff;
  buffer[index + 2] = (size >> 16) & 0xff;
  buffer[index + 3] = (size >> 24) & 0xff;
  // Update index
  index = index + 4 + size - 1;
  // Write zero
  buffer[index++] = 0x00;
  return index;
};

var serializeDBRef = function(buffer, key, value, index, depth, serializeFunctions, isArray) {
  // Write the type
  buffer[index++] = BSON.BSON_DATA_OBJECT;
  // Number of written bytes
  var numberOfWrittenBytes = !isArray
    ? buffer.write(key, index, 'utf8')
    : buffer.write(key, index, 'ascii');

  // Encode the name
  index = index + numberOfWrittenBytes;
  buffer[index++] = 0;

  var startIndex = index;
  var endIndex;

  // Serialize object
  if (null != value.db) {
    endIndex = serializeInto(
      buffer,
      {
        $ref: value.namespace,
        $id: value.oid,
        $db: value.db
      },
      false,
      index,
      depth + 1,
      serializeFunctions
    );
  } else {
    endIndex = serializeInto(
      buffer,
      {
        $ref: value.namespace,
        $id: value.oid
      },
      false,
      index,
      depth + 1,
      serializeFunctions
    );
  }

  // Calculate object size
  var size = endIndex - startIndex;
  // Write the size
  buffer[startIndex++] = size & 0xff;
  buffer[startIndex++] = (size >> 8) & 0xff;
  buffer[startIndex++] = (size >> 16) & 0xff;
  buffer[startIndex++] = (size >> 24) & 0xff;
  // Set index
  return endIndex;
};

var serializeInto = function serializeInto(
  buffer,
  object,
  checkKeys,
  startingIndex,
  depth,
  serializeFunctions,
  ignoreUndefined,
  path
) {
  startingIndex = startingIndex || 0;
  path = path || [];

  // Push the object to the path
  path.push(object);

  // Start place to serialize into
  var index = startingIndex + 4;
  // var self = this;

  // Special case isArray
  if (Array.isArray(object)) {
    // Get object keys
    for (var i = 0; i < object.length; i++) {
      var key = '' + i;
      var value = object[i];

      // Is there an override value
      if (value && value.toBSON) {
        if (typeof value.toBSON !== 'function') throw new Error('toBSON is not a function');
        value = value.toBSON();
      }

      var type = typeof value;
      if (type === 'string') {
        index = serializeString(buffer, key, value, index, true);
      } else if (type === 'number') {
        index = serializeNumber(buffer, key, value, index, true);
      } else if (type === 'boolean') {
        index = serializeBoolean(buffer, key, value, index, true);
      } else if (value instanceof Date || isDate(value)) {
        index = serializeDate(buffer, key, value, index, true);
      } else if (value === undefined) {
        index = serializeNull(buffer, key, value, index, true);
      } else if (value === null) {
        index = serializeNull(buffer, key, value, index, true);
      } else if (value['_bsontype'] === 'ObjectID' || value['_bsontype'] === 'ObjectId') {
        index = serializeObjectId(buffer, key, value, index, true);
      } else if (Buffer.isBuffer(value)) {
        index = serializeBuffer(buffer, key, value, index, true);
      } else if (value instanceof RegExp || isRegExp(value)) {
        index = serializeRegExp(buffer, key, value, index, true);
      } else if (type === 'object' && value['_bsontype'] == null) {
        index = serializeObject(
          buffer,
          key,
          value,
          index,
          checkKeys,
          depth,
          serializeFunctions,
          ignoreUndefined,
          true,
          path
        );
      } else if (type === 'object' && value['_bsontype'] === 'Decimal128') {
        index = serializeDecimal128(buffer, key, value, index, true);
      } else if (value['_bsontype'] === 'Long' || value['_bsontype'] === 'Timestamp') {
        index = serializeLong(buffer, key, value, index, true);
      } else if (value['_bsontype'] === 'Double') {
        index = serializeDouble(buffer, key, value, index, true);
      } else if (typeof value === 'function' && serializeFunctions) {
        index = serializeFunction(
          buffer,
          key,
          value,
          index,
          checkKeys,
          depth,
          serializeFunctions,
          true
        );
      } else if (value['_bsontype'] === 'Code') {
        index = serializeCode(
          buffer,
          key,
          value,
          index,
          checkKeys,
          depth,
          serializeFunctions,
          ignoreUndefined,
          true
        );
      } else if (value['_bsontype'] === 'Binary') {
        index = serializeBinary(buffer, key, value, index, true);
      } else if (value['_bsontype'] === 'Symbol') {
        index = serializeSymbol(buffer, key, value, index, true);
      } else if (value['_bsontype'] === 'DBRef') {
        index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, true);
      } else if (value['_bsontype'] === 'BSONRegExp') {
        index = serializeBSONRegExp(buffer, key, value, index, true);
      } else if (value['_bsontype'] === 'Int32') {
        index = serializeInt32(buffer, key, value, index, true);
      } else if (value['_bsontype'] === 'MinKey' || value['_bsontype'] === 'MaxKey') {
        index = serializeMinMax(buffer, key, value, index, true);
      }
    }
  } else if (object instanceof Map) {
    var iterator = object.entries();
    var done = false;

    while (!done) {
      // Unpack the next entry
      var entry = iterator.next();
      done = entry.done;
      // Are we done, then skip and terminate
      if (done) continue;

      // Get the entry values
      key = entry.value[0];
      value = entry.value[1];

      // Check the type of the value
      type = typeof value;

      // Check the key and throw error if it's illegal
      if (typeof key === 'string' && ignoreKeys.indexOf(key) === -1) {
        if (key.match(regexp) != null) {
          // The BSON spec doesn't allow keys with null bytes because keys are
          // null-terminated.
          throw Error('key ' + key + ' must not contain null bytes');
        }

        if (checkKeys) {
          if ('$' === key[0]) {
            throw Error('key ' + key + " must not start with '$'");
          } else if (~key.indexOf('.')) {
            throw Error('key ' + key + " must not contain '.'");
          }
        }
      }

      if (type === 'string') {
        index = serializeString(buffer, key, value, index);
      } else if (type === 'number') {
        index = serializeNumber(buffer, key, value, index);
      } else if (type === 'boolean') {
        index = serializeBoolean(buffer, key, value, index);
      } else if (value instanceof Date || isDate(value)) {
        index = serializeDate(buffer, key, value, index);
        // } else if (value === undefined && ignoreUndefined === true) {
      } else if (value === null || (value === undefined && ignoreUndefined === false)) {
        index = serializeNull(buffer, key, value, index);
      } else if (value['_bsontype'] === 'ObjectID' || value['_bsontype'] === 'ObjectId') {
        index = serializeObjectId(buffer, key, value, index);
      } else if (Buffer.isBuffer(value)) {
        index = serializeBuffer(buffer, key, value, index);
      } else if (value instanceof RegExp || isRegExp(value)) {
        index = serializeRegExp(buffer, key, value, index);
      } else if (type === 'object' && value['_bsontype'] == null) {
        index = serializeObject(
          buffer,
          key,
          value,
          index,
          checkKeys,
          depth,
          serializeFunctions,
          ignoreUndefined,
          false,
          path
        );
      } else if (type === 'object' && value['_bsontype'] === 'Decimal128') {
        index = serializeDecimal128(buffer, key, value, index);
      } else if (value['_bsontype'] === 'Long' || value['_bsontype'] === 'Timestamp') {
        index = serializeLong(buffer, key, value, index);
      } else if (value['_bsontype'] === 'Double') {
        index = serializeDouble(buffer, key, value, index);
      } else if (value['_bsontype'] === 'Code') {
        index = serializeCode(
          buffer,
          key,
          value,
          index,
          checkKeys,
          depth,
          serializeFunctions,
          ignoreUndefined
        );
      } else if (typeof value === 'function' && serializeFunctions) {
        index = serializeFunction(buffer, key, value, index, checkKeys, depth, serializeFunctions);
      } else if (value['_bsontype'] === 'Binary') {
        index = serializeBinary(buffer, key, value, index);
      } else if (value['_bsontype'] === 'Symbol') {
        index = serializeSymbol(buffer, key, value, index);
      } else if (value['_bsontype'] === 'DBRef') {
        index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions);
      } else if (value['_bsontype'] === 'BSONRegExp') {
        index = serializeBSONRegExp(buffer, key, value, index);
      } else if (value['_bsontype'] === 'Int32') {
        index = serializeInt32(buffer, key, value, index);
      } else if (value['_bsontype'] === 'MinKey' || value['_bsontype'] === 'MaxKey') {
        index = serializeMinMax(buffer, key, value, index);
      }
    }
  } else {
    // Did we provide a custom serialization method
    if (object.toBSON) {
      if (typeof object.toBSON !== 'function') throw new Error('toBSON is not a function');
      object = object.toBSON();
      if (object != null && typeof object !== 'object')
        throw new Error('toBSON function did not return an object');
    }

    // Iterate over all the keys
    for (key in object) {
      value = object[key];
      // Is there an override value
      if (value && value.toBSON) {
        if (typeof value.toBSON !== 'function') throw new Error('toBSON is not a function');
        value = value.toBSON();
      }

      // Check the type of the value
      type = typeof value;

      // Check the key and throw error if it's illegal
      if (typeof key === 'string' && ignoreKeys.indexOf(key) === -1) {
        if (key.match(regexp) != null) {
          // The BSON spec doesn't allow keys with null bytes because keys are
          // null-terminated.
          throw Error('key ' + key + ' must not contain null bytes');
        }

        if (checkKeys) {
          if ('$' === key[0]) {
            throw Error('key ' + key + " must not start with '$'");
          } else if (~key.indexOf('.')) {
            throw Error('key ' + key + " must not contain '.'");
          }
        }
      }

      if (type === 'string') {
        index = serializeString(buffer, key, value, index);
      } else if (type === 'number') {
        index = serializeNumber(buffer, key, value, index);
      } else if (type === 'boolean') {
        index = serializeBoolean(buffer, key, value, index);
      } else if (value instanceof Date || isDate(value)) {
        index = serializeDate(buffer, key, value, index);
      } else if (value === undefined) {
        if (ignoreUndefined === false) index = serializeNull(buffer, key, value, index);
      } else if (value === null) {
        index = serializeNull(buffer, key, value, index);
      } else if (value['_bsontype'] === 'ObjectID' || value['_bsontype'] === 'ObjectId') {
        index = serializeObjectId(buffer, key, value, index);
      } else if (Buffer.isBuffer(value)) {
        index = serializeBuffer(buffer, key, value, index);
      } else if (value instanceof RegExp || isRegExp(value)) {
        index = serializeRegExp(buffer, key, value, index);
      } else if (type === 'object' && value['_bsontype'] == null) {
        index = serializeObject(
          buffer,
          key,
          value,
          index,
          checkKeys,
          depth,
          serializeFunctions,
          ignoreUndefined,
          false,
          path
        );
      } else if (type === 'object' && value['_bsontype'] === 'Decimal128') {
        index = serializeDecimal128(buffer, key, value, index);
      } else if (value['_bsontype'] === 'Long' || value['_bsontype'] === 'Timestamp') {
        index = serializeLong(buffer, key, value, index);
      } else if (value['_bsontype'] === 'Double') {
        index = serializeDouble(buffer, key, value, index);
      } else if (value['_bsontype'] === 'Code') {
        index = serializeCode(
          buffer,
          key,
          value,
          index,
          checkKeys,
          depth,
          serializeFunctions,
          ignoreUndefined
        );
      } else if (typeof value === 'function' && serializeFunctions) {
        index = serializeFunction(buffer, key, value, index, checkKeys, depth, serializeFunctions);
      } else if (value['_bsontype'] === 'Binary') {
        index = serializeBinary(buffer, key, value, index);
      } else if (value['_bsontype'] === 'Symbol') {
        index = serializeSymbol(buffer, key, value, index);
      } else if (value['_bsontype'] === 'DBRef') {
        index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions);
      } else if (value['_bsontype'] === 'BSONRegExp') {
        index = serializeBSONRegExp(buffer, key, value, index);
      } else if (value['_bsontype'] === 'Int32') {
        index = serializeInt32(buffer, key, value, index);
      } else if (value['_bsontype'] === 'MinKey' || value['_bsontype'] === 'MaxKey') {
        index = serializeMinMax(buffer, key, value, index);
      }
    }
  }

  // Remove the path
  path.pop();

  // Final padding byte for object
  buffer[index++] = 0x00;

  // Final size
  var size = index - startingIndex;
  // Write the size of the object
  buffer[startingIndex++] = size & 0xff;
  buffer[startingIndex++] = (size >> 8) & 0xff;
  buffer[startingIndex++] = (size >> 16) & 0xff;
  buffer[startingIndex++] = (size >> 24) & 0xff;
  return index;
};

var BSON = {};

/**
 * Contains the function cache if we have that enable to allow for avoiding the eval step on each deserialization, comparison is by md5
 *
 * @ignore
 * @api private
 */
// var functionCache = (BSON.functionCache = {});

/**
 * Number BSON Type
 *
 * @classconstant BSON_DATA_NUMBER
 **/
BSON.BSON_DATA_NUMBER = 1;
/**
 * String BSON Type
 *
 * @classconstant BSON_DATA_STRING
 **/
BSON.BSON_DATA_STRING = 2;
/**
 * Object BSON Type
 *
 * @classconstant BSON_DATA_OBJECT
 **/
BSON.BSON_DATA_OBJECT = 3;
/**
 * Array BSON Type
 *
 * @classconstant BSON_DATA_ARRAY
 **/
BSON.BSON_DATA_ARRAY = 4;
/**
 * Binary BSON Type
 *
 * @classconstant BSON_DATA_BINARY
 **/
BSON.BSON_DATA_BINARY = 5;
/**
 * ObjectID BSON Type, deprecated
 *
 * @classconstant BSON_DATA_UNDEFINED
 **/
BSON.BSON_DATA_UNDEFINED = 6;
/**
 * ObjectID BSON Type
 *
 * @classconstant BSON_DATA_OID
 **/
BSON.BSON_DATA_OID = 7;
/**
 * Boolean BSON Type
 *
 * @classconstant BSON_DATA_BOOLEAN
 **/
BSON.BSON_DATA_BOOLEAN = 8;
/**
 * Date BSON Type
 *
 * @classconstant BSON_DATA_DATE
 **/
BSON.BSON_DATA_DATE = 9;
/**
 * null BSON Type
 *
 * @classconstant BSON_DATA_NULL
 **/
BSON.BSON_DATA_NULL = 10;
/**
 * RegExp BSON Type
 *
 * @classconstant BSON_DATA_REGEXP
 **/
BSON.BSON_DATA_REGEXP = 11;
/**
 * Code BSON Type
 *
 * @classconstant BSON_DATA_CODE
 **/
BSON.BSON_DATA_CODE = 13;
/**
 * Symbol BSON Type
 *
 * @classconstant BSON_DATA_SYMBOL
 **/
BSON.BSON_DATA_SYMBOL = 14;
/**
 * Code with Scope BSON Type
 *
 * @classconstant BSON_DATA_CODE_W_SCOPE
 **/
BSON.BSON_DATA_CODE_W_SCOPE = 15;
/**
 * 32 bit Integer BSON Type
 *
 * @classconstant BSON_DATA_INT
 **/
BSON.BSON_DATA_INT = 16;
/**
 * Timestamp BSON Type
 *
 * @classconstant BSON_DATA_TIMESTAMP
 **/
BSON.BSON_DATA_TIMESTAMP = 17;
/**
 * Long BSON Type
 *
 * @classconstant BSON_DATA_LONG
 **/
BSON.BSON_DATA_LONG = 18;
/**
 * Long BSON Type
 *
 * @classconstant BSON_DATA_DECIMAL128
 **/
BSON.BSON_DATA_DECIMAL128 = 19;
/**
 * MinKey BSON Type
 *
 * @classconstant BSON_DATA_MIN_KEY
 **/
BSON.BSON_DATA_MIN_KEY = 0xff;
/**
 * MaxKey BSON Type
 *
 * @classconstant BSON_DATA_MAX_KEY
 **/
BSON.BSON_DATA_MAX_KEY = 0x7f;
/**
 * Binary Default Type
 *
 * @classconstant BSON_BINARY_SUBTYPE_DEFAULT
 **/
BSON.BSON_BINARY_SUBTYPE_DEFAULT = 0;
/**
 * Binary Function Type
 *
 * @classconstant BSON_BINARY_SUBTYPE_FUNCTION
 **/
BSON.BSON_BINARY_SUBTYPE_FUNCTION = 1;
/**
 * Binary Byte Array Type
 *
 * @classconstant BSON_BINARY_SUBTYPE_BYTE_ARRAY
 **/
BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2;
/**
 * Binary UUID Type
 *
 * @classconstant BSON_BINARY_SUBTYPE_UUID
 **/
BSON.BSON_BINARY_SUBTYPE_UUID = 3;
/**
 * Binary MD5 Type
 *
 * @classconstant BSON_BINARY_SUBTYPE_MD5
 **/
BSON.BSON_BINARY_SUBTYPE_MD5 = 4;
/**
 * Binary User Defined Type
 *
 * @classconstant BSON_BINARY_SUBTYPE_USER_DEFINED
 **/
BSON.BSON_BINARY_SUBTYPE_USER_DEFINED = 128;

// BSON MAX VALUES
BSON.BSON_INT32_MAX = 0x7fffffff;
BSON.BSON_INT32_MIN = -0x80000000;

BSON.BSON_INT64_MAX = Math.pow(2, 63) - 1;
BSON.BSON_INT64_MIN = -Math.pow(2, 63);

// JS MAX PRECISE VALUES
BSON.JS_INT_MAX = 0x20000000000000; // Any integer up to 2^53 can be precisely represented by a double.
BSON.JS_INT_MIN = -0x20000000000000; // Any integer down to -2^53 can be precisely represented by a double.

// Internal long versions
// var JS_INT_MAX_LONG = Long.fromNumber(0x20000000000000); // Any integer up to 2^53 can be precisely represented by a double.
// var JS_INT_MIN_LONG = Long.fromNumber(-0x20000000000000); // Any integer down to -2^53 can be precisely represented by a double.

module.exports = serializeInto;

Output:
【漏洞类型】业务逻辑安全漏洞
【严重性】Medium
【漏洞摘要】此代码在序列化过程中未对 `_bsontype` 字段的值进行充分验证。如果遇到 `_bsontype` 字段值不被识别或无效，代码会跳过该值，不抛出任何错误。这可能导致数据在序列化过程中丢失，影响数据完整性，并可能被利用以执行未预期的行为。

【关键代码模式】与 `_bsontype` 字段相关的代码模式包括：
- `serializeObject`, `serializeInto`, `serializeCode`, `serializeFunction`, 等函数中对 `_bsontype` 字段的检查。
- 未处理 `_bsontype` 字段值不被识别的情况。

【修复方法说明】修复方法通过在每个 `_bsontype` 检查的末尾添加一个 `else if` 条件，检查 `_bsontype` 是否定义且不被识别的值，并抛出 `TypeError`。这确保了所有未识别的 `_bsontype` 值都会被检测到并抛出错误，从而提高数据完整性并防止未预期的行为。


Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
CHANGELOG.md
### 代码内容：
# Jodd Changelog

All notable changes to Jodd project are documented here.

## [n/a](https://github.com/oblac/jodd/compare/v5.0.3...master)

(work in progress :)

### Bug fixes

+ **core** - fixed special cases in `NaturalOrderComparator`, making rules a bit more strict.
+ **mail** - fixed unused `debug` and `timeout`.
+ **mail** - `RFC2822AddressParser` methods `parseToXxx()` now returns `null` for invalid emails.
+ **http** - secure connection was sending `CONNECT` string twice.

### Breaking changes

+ **mail** - `debug` and `timeout` are now applied before creating the mail server.
+ **http** - default security has been set to `TLSv1.1`.

### New Features

+ **joy** - added excluded jars for faster scanning.
+ **mail** - added custom properties.
+ **json** - added `onValue` callback for JSON serializer.
+ **json** - added `excludeEmpty` flag for JSON serializer.
+ **petite** - allow injection in the private fields of super types.

## [v5.0.3](https://github.com/oblac/jodd/compare/v5.0.2...v5.0.3)

### New Features

+ **dboom** - added detection of the quote names in annotations.
+ **dboom** - Added flags to column and table naming strategies for quote chars.

## [v5.0.2](https://github.com/oblac/jodd/compare/v5.0.1...v5.0.2)

One minor fix and one important change for the bootstrap usages.

### New Features

+ **joy** - added static method for registering joy servlet context listener.

### Bug Fixes

+ **joy** - better output of the Joy configuration that does not cut of the values.

## [v5.0.1](https://github.com/oblac/jodd/compare/v5.0.0...v5.0.1)

Minor fixes and improvements.

### New Features

+ **core*** - added the `Maybe.or(T)` method.
+ **mail** - added `receive()` and fluent builder.
+ **cli*** - parse values bundled with options (e.g. `--name=value`)

### Bug Fixes

+ **cli** - fixed the behaviour of parameters

## [v5](https://github.com/oblac/jodd/compare/v4.3.2...v5.0.0)

Welcome to Jodd 5.

Version 5 contains a great number of new features, changes, bug fixes and performance improvements. It's all new Jodd: slick as before, just better.

High-five!

## Previous releases

[v4.x](CHANGELOG_v4.md)
[v3.x](CHANGELOG_v3.md)

## 内容2
### 文件路径：
jodd-json/src/main/java/jodd/json/JsonParser.java
### 代码内容：
// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// 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.
//
// 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 HOLDER 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 jodd.json;

import jodd.introspector.ClassDescriptor;
import jodd.introspector.ClassIntrospector;
import jodd.introspector.PropertyDescriptor;
import jodd.json.meta.JsonAnnotationManager;
import jodd.json.meta.TypeData;
import jodd.util.CharArraySequence;
import jodd.util.CharUtil;
import jodd.util.StringPool;
import jodd.util.UnsafeUtil;

import java.math.BigInteger;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

/**
 * Simple, developer-friendly JSON parser. It focuses on easy usage
 * and type mappings. Uses Jodd's type converters, so it is natural
 * companion for Jodd projects.
 *

 * This JSON parser also works in {@link #lazy(boolean)} mode. This
 * mode is for top performance usage: parsing is done very, very lazy.
 * While you can use all the mappings and other tools, for best performance
 * the lazy mode should be used only with maps and lists (no special mappings).
 * Also, the performance has it's price: more memory consumption, because the
 * original input is hold until the result is in use.
 *

 * See: http://www.ietf.org/rfc/rfc4627.txt
 */
public class JsonParser extends JsonParserBase {

	public static class Defaults {

		public static final String DEFAULT_CLASS_METADATA_NAME = "__class";

		/**
		 * Flag for enabling the lazy mode.
		 */
		public static boolean lazy = false;
		/**
		 * Defines if parser will use extended paths information
		 * and path matching.
		 */
		public static boolean useAltPathsByParser = false;
		/**
		 * Default value for loose mode.
		 */
		public static boolean loose = false;

		/**
		 * Specifies if 'class' metadata is used and its value. When set, class metadata
		 * is used by {@link jodd.json.JsonSerializer} and all objects
		 * will have additional field with the class type in the resulting JSON.
		 * {@link jodd.json.JsonParser} will also consider this flag to build
		 * correct object type. If null, class information is not used.
		 */
		public static String classMetadataName = null;
	}

	/**
	 * Static ctor.
	 */
	public static JsonParser create() {
		return new JsonParser();
	}

	/**
	 * Creates a lazy implementation of the JSON parser.
	 */
	public static JsonParser createLazyOne() {
		return new JsonParser().lazy(true);
	}

	private static final char[] T_RUE = new char[] {'r', 'u', 'e'};
	private static final char[] F_ALSE = new char[] {'a', 'l', 's', 'e'};
	private static final char[] N_ULL = new char[] {'u', 'l', 'l'};

	/**
	 * Map keys.
	 */
	public static final String KEYS = "keys";
	/**
	 * Array or map values.
	 */
	public static final String VALUES = "values";

	protected int ndx = 0;
	protected char[] input;
	protected int total;
	protected Path path;
	protected boolean useAltPaths = Defaults.useAltPathsByParser;
	protected boolean lazy = Defaults.lazy;
	protected boolean looseMode = Defaults.loose;
	protected Class rootType;
	protected MapToBean mapToBean;
	private boolean notFirstObject = false;

	private final JsonAnnotationManager jsonAnnotationManager;

	public JsonParser() {
		this.text = new char[512];
		this.jsonAnnotationManager = JsonAnnotationManager.get();
	}

	/**
	 * Resets JSON parser, so it can be reused.
	 */
	protected void reset() {
		this.ndx = 0;
		this.textLen = 0;
		this.path = new Path();
		if (useAltPaths) {
			path.altPath = new Path();
		}

		if (classMetadataName != null) {
			mapToBean = createMapToBean(classMetadataName);
		}
	}

	/**
	 * Enables usage of additional paths.
	 */
	public JsonParser useAltPaths() {
		this.useAltPaths = true;
		return this;
	}

	/**
	 * Enables 'loose' mode for parsing. When 'loose' mode is enabled,
	 * JSON parsers swallows also invalid JSONs:
	 *

	 *
invalid escape character sequence is simply added to the output

	 *
strings can be quoted with single-quotes

	 *
strings can be unquoted, but may not contain escapes

	 *

	 */
	public JsonParser looseMode(final boolean looseMode) {
		this.looseMode = looseMode;
		return this;
	}

	/**
	 * Defines how JSON parser works. In non-lazy mode, the whole JSON is parsed as it is.
	 * In the lazy mode, not everything is parsed, but some things are left lazy.
	 * This way we gain performance, especially on partial usage of the whole JSON.
	 * However, be aware that parser holds the input memory until the returned
	 * objects are disposed.
	 */
	public JsonParser lazy(final boolean lazy) {
		this.lazy = lazy;
		this.mapSupplier = lazy ? LAZYMAP_SUPPLIER : HASMAP_SUPPLIER;
		this.listSupplier = lazy ? LAZYLIST_SUPPLIER : ARRAYLIST_SUPPLIER;
		return this;
	}

	// ---------------------------------------------------------------- mappings

	protected Map<Path, Class> mappings;

	/**
	 * Maps a class to JSONs root.
	 */
	public JsonParser map(final Class target) {
		rootType = target;
		return this;
	}

	/**
	 * Maps a class to given path. For arrays, append values
	 * to the path to specify component type (if not specified by
	 * generics).
	 */
	public JsonParser map(final String path, final Class target) {
		if (path == null) {
			rootType = target;
			return this;
		}
		if (mappings == null) {
			mappings = new HashMap<>();
		}
		mappings.put(Path.parse(path), target);
		return this;
	}

	/**
	 * Replaces type with mapped type for current path.
	 */
	protected Class replaceWithMappedTypeForPath(final Class target) {
		if (mappings == null) {
			return target;
		}

		Class newType;

		// first try alt paths

		Path altPath = path.getAltPath();

		if (altPath != null) {
			if (!altPath.equals(path)) {
				newType = mappings.get(altPath);

				if (newType != null) {
					return newType;
				}
			}
		}

		// now check regular paths

		newType = mappings.get(path);

		if (newType != null) {
			return newType;
		}

		return target;
	}

	// ---------------------------------------------------------------- converters

	protected Map<Path, ValueConverter> convs;

	/**
	 * Defines {@link jodd.json.ValueConverter} to use on given path.
	 */
	public JsonParser withValueConverter(final String path, final ValueConverter valueConverter) {
		if (convs == null) {
			convs = new HashMap<>();
		}
		convs.put(Path.parse(path), valueConverter);
		return this;
	}

	/**
	 * Lookups for value converter for current path.
	 */
	protected ValueConverter lookupValueConverter() {
		if (convs == null) {
			return null;
		}
		return convs.get(path);
	}

	// ---------------------------------------------------------------- class meta data name

	protected String classMetadataName = Defaults.classMetadataName;

	/**
	 * Sets local class meta-data name.
	 *

	 * Note that by using the class meta-data name you may expose a security hole in case untrusted source
	 * manages to specify a class that is accessible through class loader and exposes set of methods and/or fields,
	 * access of which opens an actual security hole. Such classes are known as “deserialization gadget”s.
	 *
	 * Because of this, use of "default typing" is not encouraged in general, and in particular is recommended against
	 * if the source of content is not trusted. Conversely, default typing may be used for processing content in
	 * cases where both ends (sender and receiver) are controlled by same entity.
	 */
	public JsonParser setClassMetadataName(final String name) {
		classMetadataName = name;
		return this;
	}

	/**
	 * Sets usage of default class meta-data name.
	 * Using it may introduce a security hole, see {@link #setClassMetadataName(String)} for more details.
	 * @see #setClassMetadataName(String)
	 */
	public JsonParser withClassMetadata(final boolean useMetadata) {
		if (useMetadata) {
			classMetadataName = Defaults.DEFAULT_CLASS_METADATA_NAME;
		}
		else {
			classMetadataName = null;
		}
		return this;
	}

	// ---------------------------------------------------------------- parse

	/**
	 * Parses input JSON as given type.
	 */
	@SuppressWarnings("unchecked")
	public <T> T parse(final String input, final Class<T> targetType) {
		rootType = targetType;
		return _parse(UnsafeUtil.getChars(input));
	}

	/**
	 * Parses input JSON to {@link JsonObject}, special case of {@link #parse(String, Class)}.
	 */
	public JsonObject parseAsJsonObject(final String input) {
		return new JsonObject(parse(input));
	}

	/**
	 * Parses input JSON to {@link JsonArray}, special case of parsing.
	 */
	public JsonArray parseAsJsonArray(final String input) {
		return new JsonArray(parse(input));
	}

	/**
	 * Parses input JSON to a list with specified component type.
	 */
	public <T> List<T> parseAsList(final String string, final Class<T> componentType) {
		return new JsonParser()
			.map(JsonParser.VALUES, componentType)
			.parse(string);
	}

	/**
	 * Parses input JSON to a list with specified key and value types.
	 */
	public <K, V> Map<K, V> parseAsMap(
		final String string, final Class<K> keyType, final Class<V> valueType) {

		return new JsonParser()
			.map(JsonParser.KEYS, keyType)
			.map(JsonParser.VALUES, valueType)
			.parse(string);
	}

	/**
	 * Parses input JSON string.
	 */
	public <T> T parse(final String input) {
		return _parse(UnsafeUtil.getChars(input));
	}

	/**
	 * Parses input JSON as given type.
	 */
	@SuppressWarnings("unchecked")
	public <T> T parse(final char[] input, final Class<T> targetType) {
		rootType = targetType;
		return _parse(input);
	}

	/**
	 * Parses input JSON char array.
	 */
	public <T> T parse(final char[] input) {
		return _parse(input);
	}

	private <T> T _parse(final char[] input) {
		this.input = input;
		this.total = input.length;

		reset();

		skipWhiteSpaces();

		Object value;

		try {
			value = parseValue(rootType, null, null);
		}
		catch (IndexOutOfBoundsException iofbex) {
			syntaxError("End of JSON");
			return null;
		}

		skipWhiteSpaces();

		if (ndx != total) {
			syntaxError("Trailing chars");
			return null;
		}

		if (lazy) {
			// lets resolve root lazy values
			value = resolveLazyValue(value);
		}

		// convert map to target type

		if (classMetadataName != null && rootType == null) {
			if (value instanceof Map) {
				Map map = (Map) value;

				value = mapToBean.map2bean(map, null);
			}
		}

		return (T) value;
	}

	// ---------------------------------------------------------------- parser

	/**
	 * Parses a JSON value.
	 * @param targetType target type to convert, may be null
	 * @param componentType component type for maps and arrays, may be null
	 */
	protected Object parseValue(final Class targetType, final Class keyType, final Class componentType) {
		final ValueConverter valueConverter;

		final char c = input[ndx];

		switch (c) {
			case '\'':
				if (!looseMode) {
					break;
				}
			case '"':
				ndx++;
				Object string = parseStringContent(c);

				valueConverter = lookupValueConverter();
				if (valueConverter != null) {
					return valueConverter.convert(string);
				}

				if (targetType != null && targetType != String.class) {
					string = convertType(string, targetType);
				}
				return string;

			case '{':
				ndx++;
				if (lazy) {
					if (notFirstObject) {
						final Object value = new ObjectParser(this, targetType, keyType, componentType);

						skipObject();

						return value;
					}
					else {
						notFirstObject = true;
					}
				}

				return parseObjectContent(targetType, keyType, componentType);

			case '[':
				ndx++;
				return parseArrayContent(targetType, componentType);

			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			case '-':
				Object number = parseNumber();

				valueConverter = lookupValueConverter();
				if (valueConverter != null) {
					return valueConverter.convert(number);
				}

				if (targetType != null) {
					number = convertType(number, targetType);
				}
				return number;

			case 'n':
				ndx++;
				if (match(N_ULL)) {
					valueConverter = lookupValueConverter();
					if (valueConverter != null) {
						return valueConverter.convert(null);
					}
					return null;
				}
				break;

			case 't':
				ndx++;
				if (match(T_RUE)) {
					Object value = Boolean.TRUE;

					valueConverter = lookupValueConverter();
					if (valueConverter != null) {
						return valueConverter.convert(value);
					}

					if (targetType != null) {
						value = convertType(value, targetType);
					}
					return value;
				}
				break;

			case 'f':
				ndx++;
				if (match(F_ALSE)) {
					Object value = Boolean.FALSE;

					valueConverter = lookupValueConverter();
					if (valueConverter != null) {
						return valueConverter.convert(value);
					}

					if (targetType != null) {
						value = convertType(value, targetType);
					}
					return value;
				}
				break;
		}

		if (looseMode) {
			// try to parse unquoted string
			Object string = parseUnquotedStringContent();

			valueConverter = lookupValueConverter();
			if (valueConverter != null) {
				return valueConverter.convert(string);
			}

			if (targetType != null && targetType != String.class) {
				string = convertType(string, targetType);
			}
			return string;
		}

		syntaxError("Invalid char: " + input[ndx]);
		return null;
	}

	// ---------------------------------------------------------------- lazy

	/**
	 * Resolves lazy value during the parsing runtime.
	 */
	private Object resolveLazyValue(Object value) {
		if (value instanceof Supplier) {
			value = ((Supplier)value).get();
		}
		return value;
	}

	/**
	 * Skips over complete object. It is not parsed, just skipped. It will be
	 * parsed later, but oonly if required.
	 */
	private void skipObject() {
		int bracketCount = 1;
		boolean insideString = false;

		while (ndx < total) {
			final char c = input[ndx];

			if (insideString) {
				if (c == '\"') {
					insideString = false;
				}
			}
			else {
				if (c == '\"') {
					insideString = true;
				}
				if (c == '{') {
					bracketCount++;
				} else if (c == '}') {
					bracketCount--;
					if (bracketCount == 0) {
						ndx++;
						return;
					}
				}
			}
			ndx++;
		}
	}

	// ---------------------------------------------------------------- string

	protected char[] text;
	protected int textLen;

	/**
	 * Parses a string.
	 */
	protected String parseString() {
		char quote = '\"';
		if (looseMode) {
			quote = consumeOneOf('\"', '\'');
			if (quote == 0) {
				return parseUnquotedStringContent();
			}
		} else {
			consume(quote);
		}

		return parseStringContent(quote);
	}

	/**
	 * Parses string content, once when starting quote has been consumed.
	 */
	protected String parseStringContent(final char quote) {
		final int startNdx = ndx;

		// roll-out until the end of the string or the escape char
		while (true) {
			final char c = input[ndx];

			if (c == quote) {
				// no escapes found, just use existing string
				ndx++;
				return new String(input, startNdx, ndx - 1 - startNdx);
			}

			if (c == '\\') {
				break;
			}

			ndx++;
		}

		// escapes found, proceed differently

		textLen = ndx - startNdx;

		growEmpty();

//		for (int i = startNdx, j = 0; j < textLen; i++, j++) {
//			text[j] = input[i];
//		}
		System.arraycopy(input, startNdx, text, 0, textLen);

		// escape char, process everything until the end
		while (true) {
			char c = input[ndx];

			if (c == quote) {
				// done
				ndx++;
				final String str = new String(text, 0, textLen);
				textLen = 0;
				return str;
			}

			if (c == '\\') {
				// escape char found
				ndx++;

				c = input[ndx];

				switch (c) {
					case '\"' : c = '\"'; break;
					case '\\' : c = '\\'; break;
					case '/' : c = '/'; break;
					case 'b' : c = '\b'; break;
					case 'f' : c = '\f'; break;
					case 'n' : c = '\n'; break;
					case 'r' : c = '\r'; break;
					case 't' : c = '\t'; break;
					case 'u' :
						ndx++;
						c = parseUnicode();
						break;
					default:
						if (looseMode) {
							if (c != '\'') {
								c = '\\';
								ndx--;
							}
						}
						else {
							syntaxError("Invalid escape char: " + c);
						}
				}
			}

			text[textLen] = c;

			textLen++;

			growAndCopy();

			ndx++;
		}
	}

	/**
	 * Grows empty text array.
	 */
	protected void growEmpty() {
		if (textLen >= text.length) {
			int newSize = textLen << 1;

			text = new char[newSize];
		}
	}

	/**
	 * Grows text array when {@code text.length == textLen}.
	 */
	protected void growAndCopy() {
		if (textLen == text.length) {
			int newSize = text.length << 1;

			char[] newText = new char[newSize];

			if (textLen > 0) {
				System.arraycopy(text, 0, newText, 0, textLen);
			}

			text = newText;
		}
	}

	/**
	 * Parses 4 characters and returns unicode character.
	 */
	protected char parseUnicode() {
		int i0 = CharUtil.hex2int(input[ndx++]);
		int i1 = CharUtil.hex2int(input[ndx++]);
		int i2 = CharUtil.hex2int(input[ndx++]);
		int i3 = CharUtil.hex2int(input[ndx]);

		return (char) ((i0 << 12) + (i1 << 8) + (i2 << 4) + i3);
	}

	// ---------------------------------------------------------------- un-quoted

	private final static char[] UNQOUTED_DELIMETERS = ",:[]{}\\\"'".toCharArray();

	/**
	 * Parses un-quoted string content.
	 */
	protected String parseUnquotedStringContent() {
		final int startNdx = ndx;

		while (true) {
			final char c = input[ndx];

			if (c <= ' ' || CharUtil.equalsOne(c, UNQOUTED_DELIMETERS)) {
				final int currentNdx = ndx;

				// done
				skipWhiteSpaces();

				return new String(input, startNdx, currentNdx - startNdx);
			}

			ndx++;
		}
	}

	// ---------------------------------------------------------------- number

	/**
	 * Parses JSON numbers.
	 */
	protected Number parseNumber() {
		final int startIndex = ndx;

		char c = input[ndx];

		boolean isDouble = false;
		boolean isExp = false;

		if (c == '-') {
			ndx++;
		}

		while (true) {
			if (isEOF()) {
				break;
			}

			c = input[ndx];

			if (c >= '0' && c <= '9') {
				ndx++;
				continue;
			}
			if (c <= 32) {		// white space
				break;
			}
			if (c == ',' || c == '}' || c == ']') {	// delimiter
				break;
			}

			if (c == '.') {
				isDouble = true;
			}
			else if (c == 'e' || c == 'E') {
				isExp = true;
			}
			ndx++;
		}

		final String value = new String(input, startIndex, ndx - startIndex);

		if (isDouble) {
			return Double.valueOf(value);
		}

		long longNumber;

		if (isExp) {
			longNumber = Double.valueOf(value).longValue();
		}
		else {
			if (value.length() >= 19) {
				// if string is 19 chars and longer, it can be over the limit
				BigInteger bigInteger = new BigInteger(value);

				if (isGreaterThenLong(bigInteger)) {
					return bigInteger;
				}
				longNumber = bigInteger.longValue();
			}
			else {
				longNumber = Long.parseLong(value);
			}
		}

		if ((longNumber >= Integer.MIN_VALUE) && (longNumber <= Integer.MAX_VALUE)) {
			return Integer.valueOf((int) longNumber);
		}
		return Long.valueOf(longNumber);
	}

	private static boolean isGreaterThenLong(final BigInteger bigInteger) {
		if (bigInteger.compareTo(MAX_LONG) > 0) {
			return true;
		}
		if (bigInteger.compareTo(MIN_LONG) < 0) {
			return true;
		}
		return false;
	}

	private static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
	private static final BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);

	// ---------------------------------------------------------------- array

	/**
	 * Parses arrays, once when open bracket has been consumed.
	 */
	protected Object parseArrayContent(Class targetType, Class componentType) {
		// detect special case

		if (targetType == Object.class) {
			targetType = List.class;
		}

		// continue

		targetType = replaceWithMappedTypeForPath(targetType);

		if (componentType == null && targetType != null && targetType.isArray()) {
			componentType = targetType.getComponentType();
		}

		path.push(VALUES);

		componentType = replaceWithMappedTypeForPath(componentType);

		Collection<Object> target = newArrayInstance(targetType);

		boolean koma = false;

		mainloop:
		while (true) {
			skipWhiteSpaces();

			char c = input[ndx];

			if (c == ']') {
				if (koma) {
					syntaxError("Trailing comma");
				}

				ndx++;
				path.pop();
				return target;
			}

			Object value = parseValue(componentType, null, null);

			target.add(value);

			skipWhiteSpaces();

			c = input[ndx];

			switch (c) {
				case ']': ndx++; break mainloop;
				case ',': ndx++; koma = true; break;
				default: syntaxError("Invalid char: expected ] or ,");
			}

		}

		path.pop();

		if (targetType != null) {
			return convertType(target, targetType);
		}

		return target;
	}

	// ---------------------------------------------------------------- object

	/**
	 * Parses object, once when open bracket has been consumed.
	 */
	protected Object parseObjectContent(Class targetType, Class valueKeyType, Class valueType) {
		// detect special case

		if (targetType == Object.class) {
			targetType = Map.class;
		}

		// continue

		targetType = replaceWithMappedTypeForPath(targetType);

		Object target;
		boolean isTargetTypeMap = true;
		boolean isTargetRealTypeMap = true;
		ClassDescriptor targetTypeClassDescriptor = null;
		TypeData typeData = null;

		if (targetType != null) {
			targetTypeClassDescriptor = ClassIntrospector.get().lookup(targetType);

			// find if the target is really a map
			// because when classMetadataName != null we are forcing
			// map usage locally in this method

			isTargetRealTypeMap = targetTypeClassDescriptor.isMap();

			typeData = jsonAnnotationManager.lookupTypeData(targetType);
		}

		if (isTargetRealTypeMap) {
			// resolve keys only for real maps
			path.push(KEYS);
			valueKeyType = replaceWithMappedTypeForPath(valueKeyType);
			path.pop();
		}

		if (classMetadataName == null) {
			// create instance of target type, no 'class' information
			target = newObjectInstance(targetType);

			isTargetTypeMap = isTargetRealTypeMap;
		} else {
			// all beans will be created first as a map
			target = mapSupplier.get();
		}

		boolean koma = false;

		mainloop:
		while (true) {
			skipWhiteSpaces();

			char c = input[ndx];

			if (c == '}') {
				if (koma) {
					syntaxError("Trailing comma");
				}

				ndx++;
				break;
			}

			koma = false;

			String key = parseString();
			String keyOriginal = key;

			skipWhiteSpaces();

			consume(':');

			skipWhiteSpaces();

			// read the type of the simple property

			PropertyDescriptor pd = null;
			Class propertyType = null;
			Class keyType = null;
			Class componentType = null;

			// resolve simple property

			if (!isTargetRealTypeMap) {
				// replace key with real property value
				key = jsonAnnotationManager.resolveRealName(targetType, key);
			}

			if (!isTargetTypeMap) {
				pd = targetTypeClassDescriptor.getPropertyDescriptor(key, true);

				if (pd != null) {
					propertyType = pd.getType();
					keyType = pd.resolveKeyType(true);
					componentType = pd.resolveComponentType(true);
				}
			}

			Object value;

			if (!isTargetTypeMap) {
				// *** inject into bean
					path.push(key);

					value = parseValue(propertyType, keyType, componentType);

					path.pop();

				if (typeData.rules.match(keyOriginal, !typeData.strict)) {

					if (pd != null) {
						if (lazy) {
							// need to resolve lazy value before injecting objects into it
							value = resolveLazyValue(value);
						}

						// only inject values if target property exist
						injectValueIntoObject(target, pd, value);
					}
				}
			}
			else {
				Object keyValue = key;

				if (valueKeyType != null) {
					keyValue = convertType(key, valueKeyType);
				}

				// *** add to map
				if (isTargetRealTypeMap) {
					path.push(VALUES, key);

					valueType = replaceWithMappedTypeForPath(valueType);
				} else {
					path.push(key);
				}

				value = parseValue(valueType, null, null);

				path.pop();

				((Map) target).put(keyValue, value);
			}

			skipWhiteSpaces();

			c = input[ndx];

			switch (c) {
				case '}': ndx++; break mainloop;
				case ',': ndx++; koma = true; break;
				default: syntaxError("Invalid char: expected } or ,");
			}
		}

		// done

		// convert Map to target type
		if (classMetadataName != null) {
			target = mapToBean.map2bean((Map) target, targetType);
		}

		return target;
	}

	// ---------------------------------------------------------------- scanning tools

	/**
	 * Consumes char at current position. If char is different, throws the exception.
	 */
	protected void consume(final char c) {
		if (input[ndx] != c) {
			syntaxError("Invalid char: expected " + c);
		}

		ndx++;
	}

	/**
	 * Consumes one of the allowed char at current position.
	 * If char is different, return 0.
	 * If matched, returns matched char.
	 */
	protected char consumeOneOf(final char c1, final char c2) {
		char c = input[ndx];

		if ((c != c1) && (c != c2)) {
			return 0;
		}

		ndx++;

		return c;
	}

	/**
	 * Returns true if scanning is at the end.
	 */
	protected boolean isEOF() {
		return ndx >= total;
	}

	/**
	 * Skips whitespaces. For the simplification, whitespaces are
	 * considered any characters less or equal to 32 (space).
	 */
	protected final void skipWhiteSpaces() {
		while (true) {
			if (isEOF()) {
				return;
			}
			if (input[ndx] > 32) {
				return;
			}
			ndx++;
		}
    }

	/**
	 * Matches char buffer with content on given location.
	 */
	protected final boolean match(final char[] target) {
		for (char c : target) {
			if (input[ndx] != c) {
				return false;
			}
			ndx++;
		}

		return true;
	}

	// ---------------------------------------------------------------- error

	/**
	 * Throws {@link jodd.json.JsonException} indicating a syntax error.
	 */
	protected void syntaxError(final String message) {
		String left = "...";
		String right = "...";
		int offset = 10;

		int from = ndx - offset;
		if (from < 0) {
			from = 0;
			left = StringPool.EMPTY;
		}

		int to = ndx + offset;
		if (to > input.length) {
			to = input.length;
			right = StringPool.EMPTY;
		}

		final CharSequence str = CharArraySequence.of(input, from, to - from);

		throw new JsonException(
				"Syntax error! " + message + "\n" +
				"offset: " + ndx + " near: \"" + left + str + right + "\"");
	}

}
## 内容3
### 文件路径：
jodd-json/src/main/java/jodd/json/JsonParserBase.java
### 代码内容：
// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// 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.
//
// 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 HOLDER 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 jodd.json;

import jodd.introspector.ClassDescriptor;
import jodd.introspector.ClassIntrospector;
import jodd.introspector.CtorDescriptor;
import jodd.introspector.PropertyDescriptor;
import jodd.introspector.Setter;
import jodd.typeconverter.TypeConverterManager;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

/**
 * Just a base class of {@link jodd.json.JsonParser} that contains
 * various utilities, to reduce the size of a parser.
 */
public abstract class JsonParserBase {

	protected static final Supplier<Map> HASMAP_SUPPLIER = LinkedHashMap::new;
	protected static final Supplier<Map> LAZYMAP_SUPPLIER = LazyMap::new;

	protected static final Supplier<List> ARRAYLIST_SUPPLIER = ArrayList::new;
	protected static final Supplier<List> LAZYLIST_SUPPLIER = LazyList::new;

	protected Supplier<Map> mapSupplier = HASMAP_SUPPLIER;
	protected Supplier<List> listSupplier = ARRAYLIST_SUPPLIER;

	/**
	 * Creates new instance of {@link jodd.json.MapToBean}.
	 */
	protected MapToBean createMapToBean(final String classMetadataName) {
		return new MapToBean(this, classMetadataName);
	}

	// ---------------------------------------------------------------- object tools

	/**
	 * Creates new type for JSON array objects.
	 * It returns a collection.
	 * Later, the collection will be converted into the target type.
	 */
	@SuppressWarnings("unchecked")
	protected Collection<Object> newArrayInstance(final Class targetType) {
		if (targetType == null ||
			targetType == List.class ||
			targetType == Collection.class ||
			targetType.isArray()) {

			return listSupplier.get();
		}

		if (targetType == Set.class) {
			return new HashSet<>();
		}

		try {
			return (Collection<Object>) targetType.getDeclaredConstructor().newInstance();
		} catch (Exception e) {
			throw new JsonException(e);
		}
	}

	/**
	 * Creates new object or a HashMap if type is not specified.
	 */
	protected Object newObjectInstance(final Class targetType) {
		if (targetType == null ||
			targetType == Map.class) {

			return mapSupplier.get();
		}

		ClassDescriptor cd = ClassIntrospector.get().lookup(targetType);

		CtorDescriptor ctorDescriptor = cd.getDefaultCtorDescriptor(true);
		if (ctorDescriptor == null) {
			throw new JsonException("Default ctor not found for: " + targetType.getName());
		}

		try {
//			return ClassUtil.newInstance(targetType);
			return ctorDescriptor.getConstructor().newInstance();
		} catch (Exception e) {
			throw new JsonException(e);
		}
	}

	/**
	 * Injects value into the targets property.
	 */
	protected void injectValueIntoObject(final Object target, final PropertyDescriptor pd, final Object value) {
		Object convertedValue = value;

		if (value != null) {
			Class targetClass = pd.getType();

			convertedValue = convertType(value, targetClass);
		}

		try {
			Setter setter = pd.getSetter(true);
			if (setter != null) {
				setter.invokeSetter(target, convertedValue);
			}
		} catch (Exception ex) {
			throw new JsonException(ex);
		}
	}

	/**
	 * Converts type of the given value.
	 */
	protected Object convertType(final Object value, final Class targetType) {
		Class valueClass = value.getClass();

		if (valueClass == targetType) {
			return value;
		}

		try {
			return TypeConverterManager.get().convertType(value, targetType);
		}
		catch (Exception ex) {
			throw new JsonException("Type conversion failed", ex);
		}
	}

}
## 内容4
### 文件路径：
jodd-json/src/main/java/jodd/json/MapToBean.java
### 代码内容：
// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// 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.
//
// 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 HOLDER 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 jodd.json;

import jodd.introspector.ClassDescriptor;
import jodd.introspector.ClassIntrospector;
import jodd.introspector.PropertyDescriptor;
import jodd.introspector.Setter;
import jodd.typeconverter.TypeConverterManager;
import jodd.util.ClassLoaderUtil;
import jodd.util.ClassUtil;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Map to bean converter.
 * Used when parsing with class metadata enabled.
 */
public class MapToBean {

	protected boolean declared = true;
	protected final JsonParserBase jsonParser;
	protected final String classMetadataName;

	public MapToBean(final JsonParserBase jsonParser, final String classMetadataName) {
		this.jsonParser = jsonParser;
		this.classMetadataName = classMetadataName;
	}

	/**
	 * Converts map to target type.
	 */
	public Object map2bean(final Map map, Class targetType) {
		Object target = null;

		// create targets type
		String className = (String) map.get(classMetadataName);

		if (className == null) {
			if (targetType == null) {
				// nothing to do, no information about target type found
				target = map;
			}
		}
		else {
			try {
				targetType = ClassLoaderUtil.loadClass(className);
			} catch (ClassNotFoundException cnfex) {
				throw new JsonException(cnfex);
			}
		}

		if (target == null) {
			target = jsonParser.newObjectInstance(targetType);
		}

		ClassDescriptor cd = ClassIntrospector.get().lookup(target.getClass());

		boolean targetIsMap = target instanceof Map;

		for (Object key : map.keySet()) {
			String keyName = key.toString();

			if (classMetadataName != null) {
				if (keyName.equals(classMetadataName)) {
					continue;
				}
			}

			PropertyDescriptor pd = cd.getPropertyDescriptor(keyName, declared);

			if (!targetIsMap && pd == null) {
				// target property does not exist, continue
				continue;
			}

			// value is one of JSON basic types, like Number, Map, List...
			Object value = map.get(key);

			Class propertyType = pd == null ? null : pd.getType();
			Class componentType = pd == null ? null : pd.resolveComponentType(true);

			if (value != null) {
				if (value instanceof List) {
					if (componentType != null && componentType != String.class) {
						value = generifyList((List) value, componentType);
					}
				}
				else if (value instanceof Map) {
					// if the value we want to inject is a Map...
					if (!ClassUtil.isTypeOf(propertyType, Map.class)) {
						// ... and if target is NOT a map
						value = map2bean((Map) value, propertyType);
					}
					else {
						// target is also a Map, but we might need to generify it
						Class keyType = pd == null ? null : pd.resolveKeyType(true);

						if (keyType != String.class || componentType != String.class) {
							// generify
							value = generifyMap((Map) value, keyType, componentType);
						}
					}
				}
			}

			if (targetIsMap) {
				((Map)target).put(keyName, value);
			}
			else {
				try {
					setValue(target, pd, value);
				} catch (Exception ignore) {
					ignore.printStackTrace();
				}
			}
		}

		return target;
	}

	/**
	 * Converts type of all list elements to match the component type.
	 */
	private Object generifyList(final List list, final Class componentType) {
		for (int i = 0; i < list.size(); i++) {
			Object element = list.get(i);

			if (element != null) {
				if (element instanceof Map) {
					Object bean = map2bean((Map) element, componentType);
					list.set(i, bean);
				} else {
					Object value = convert(element, componentType);
					list.set(i, value);
				}
			}
		}

		return list;
	}

	/**
	 * Sets the property value.
	 */
	private void setValue(final Object target, final PropertyDescriptor pd, Object value) throws InvocationTargetException, IllegalAccessException {
		Class propertyType;

		Setter setter = pd.getSetter(true);
		if (setter != null) {
			if (value != null) {
				propertyType = setter.getSetterRawType();
				value = jsonParser.convertType(value, propertyType);
			}
			setter.invokeSetter(target, value);
		}
	}

	/**
	 * Change map elements to match key and value types.
	 */
	protected <K,V> Map<K, V> generifyMap(final Map<Object, Object> map, final Class<K> keyType, final Class<V> valueType) {

		if (keyType == String.class) {
			// only value type is changed, we can make value replacements
			for (Map.Entry<Object, Object> entry : map.entrySet()) {
				Object value = entry.getValue();
				Object newValue = convert(value, valueType);

				if (value != newValue) {
					entry.setValue(newValue);
				}
			}
			return (Map<K, V>) map;
		}

		// key is changed too, we need a new map
		Map<K, V> newMap = new HashMap<>(map.size());

		for (Map.Entry<Object, Object> entry : map.entrySet()) {
			Object key = entry.getKey();
			Object newKey = convert(key, keyType);

			Object value = entry.getValue();
			Object newValue = convert(value, valueType);

			newMap.put((K)newKey, (V)newValue);
		}

		return newMap;
	}

	protected Object convert(final Object value, final Class targetType) {
		Class valueClass = value.getClass();

		if (valueClass == targetType) {
			return value;
		}

		if (value instanceof Map) {
			if (targetType == Map.class) {
				return value;
			}

			return map2bean((Map) value, targetType);
		}

		try {
			return TypeConverterManager.get().convertType(value, targetType);
		}
		catch (Exception ex) {
			throw new JsonException("Type conversion failed", ex);
		}
	}

}

Output:
【漏洞类型】反序列化漏洞
【严重性】High
【漏洞摘要】代码中存在反序列化漏洞。具体表现为在`JsonParser`类的`setClassMetadataName`方法中，允许用户通过`classMetadataName`字段指定任意类名，然后通过`ClassLoaderUtil.loadClass`方法加载该类。如果允许不受信任的用户输入，那么他们可以指定包含恶意代码的类名，从而在反序列化过程中执行任意代码。这一行为被称为“反序列化 gadget”攻击。
【关键代码模式】
- 类名加载：`ClassLoaderUtil.loadClass(className)`
- 类元数据名称设置：`setClassMetadataName(String name)`
- 反序列化转换：`map2bean(Map map, Class targetType)`
【修复方法说明】通过添加`allowClass`方法来白名单允许的类名，修复了反序列化漏洞。这限制了可以被加载的类名，从而防止了不受信任的类名被加载，降低了攻击面。