[JavaScript Debugger, script debugger] WEB-70558 support reading of array of object or single object value

In r1359167 Google introduced a breaking change: the type of the `Debugger.scriptParsed.debugSymbol` field was changed from `Debugger.DebugSymbols` to `array[Debugger.DebugSymbols]`.

To provide backward compatibility with old browsers, this fix introduces a new reader method `readObjectArrayOrSingleObject` which used if some object property marked with `JsonArray` annotation.


(cherry picked from commit 7847455a7c59cf7c7af3de9b991f2b6ca51928e7)

IJ-CR-150473

GitOrigin-RevId: ae6d2a42490bdcbf867d2727ce8207695d6ee3c5
This commit is contained in:
Shumaf Lovpache
2024-11-28 17:54:59 +02:00
committed by intellij-monorepo-bot
parent 714c16f197
commit dc39c7d32d
10 changed files with 44 additions and 19 deletions

View File

@@ -22,7 +22,14 @@ import org.jetbrains.annotations.ApiStatus
annotation class JsonField(
val allowAnyPrimitiveValue: Boolean = false, // read any primitive value as String (true as true, number as string - don't try to parse)
val allowAnyPrimitiveValueAndMap: Boolean = false,
val primitiveValue: String = "")
val primitiveValue: String = "",
)
@ApiStatus.Internal
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
annotation class JsonArray(
val allowSingleObject: Boolean = false, // when schema changes from {...} to [{...}] we need to handle both cases
)
@ApiStatus.Internal
@Target(AnnotationTarget.CLASS)

View File

@@ -134,6 +134,15 @@ public final class JsonReaders {
}
}
public static <T> List<T> readObjectArrayOrSingleObject(@NotNull JsonReaderEx reader, @NotNull ObjectFactory<? extends T> factory) {
if (reader.peek() == JsonToken.BEGIN_OBJECT) {
return Collections.singletonList(factory.read(reader));
}
else {
return readObjectArray(reader, factory);
}
}
public static <T> List<T> readObjectArray(@NotNull JsonReaderEx reader, @NotNull ObjectFactory<? extends T> factory) {
if (reader.peek() == JsonToken.NULL) {
reader.skipValue();

View File

@@ -1,6 +1,6 @@
package org.jetbrains.protocolReader
internal class ArrayReader(private val componentParser: ValueReader, private val isList: Boolean) : ValueReader() {
internal class ArrayReader(private val componentParser: ValueReader, private val isList: Boolean, private val allowSingleObject: Boolean) : ValueReader() {
override fun appendFinishedValueTypeName(out: TextOutput) {
if (isList) {
out.append("List<")
@@ -17,18 +17,24 @@ internal class ArrayReader(private val componentParser: ValueReader, private val
out.append('>')
}
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, out: TextOutput) {
beginReadCall("ObjectArray", subtyping, out)
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, allowSingleValue: Boolean, out: TextOutput) {
val readPostfix = if (allowSingleValue) {
"ObjectArrayOrSingleObject"
}
else {
"ObjectArray"
}
beginReadCall(readPostfix, subtyping, out)
out
.comma()
// the trick with shadowing isn't very good, but at least it's simple
.append("WrapperFactory { $READER_NAME -> ")
componentParser.writeArrayReadCode(scope, subtyping, out)
componentParser.writeArrayReadCode(scope, subtyping, allowSingleObject, out)
out.append("}")
out.append(')')
}
override fun writeReadCode(scope: ClassScope, subtyping: Boolean, out: TextOutput) {
componentParser.writeArrayReadCode(scope, subtyping, out)
componentParser.writeArrayReadCode(scope, subtyping, allowSingleObject, out)
}
}

View File

@@ -5,7 +5,7 @@ internal class EnumReader(private val enumClass: Class<Enum<*>>) : ValueReader()
out.append(enumClass.canonicalName)
}
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, out: TextOutput) {
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, allowSingleValue: Boolean, out: TextOutput) {
beginReadCall("EnumArray", subtyping, out)
out.comma().append(enumClass.canonicalName).append("::class.java)")
}

View File

@@ -2,10 +2,7 @@
package org.jetbrains.protocolReader
import org.jetbrains.io.JsonReaderEx
import org.jetbrains.jsonProtocol.JsonField
import org.jetbrains.jsonProtocol.JsonSubtype
import org.jetbrains.jsonProtocol.Optional
import org.jetbrains.jsonProtocol.StringIntPair
import org.jetbrains.jsonProtocol.*
import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
@@ -157,7 +154,7 @@ internal class InterfaceReader(private val typeToTypeHandler: LinkedHashMap<Clas
type == Any::class.java -> RAW_STRING_OR_MAP_PARSER
type == JsonReaderEx::class.java -> JSON_PARSER
type == StringIntPair::class.java -> STRING_INT_PAIR_PARSER
type.isArray -> ArrayReader(getFieldTypeParser(null, type.componentType, false, null), false)
type.isArray -> ArrayReader(getFieldTypeParser(null, type.componentType, false, null), false, member?.annotation<JsonArray>()?.allowSingleObject == true)
type.isEnum -> EnumReader(type as Class<Enum<*>>)
else -> {
val ref = getTypeRef(type)
@@ -176,7 +173,7 @@ internal class InterfaceReader(private val typeToTypeHandler: LinkedHashMap<Clas
}
}
val componentParser = getFieldTypeParser(null, argumentType, false, method)
return if (isList) ArrayReader(componentParser, true) else MapReader(componentParser)
return if (isList) ArrayReader(componentParser, true, member?.annotation<JsonArray>()?.allowSingleObject == true) else MapReader(componentParser)
}
else {
throw UnsupportedOperationException("Method return type $type (generic) not supported")

View File

@@ -20,7 +20,7 @@ internal class MapReader(private val componentParser: ValueReader) : ValueReader
out.append(')')
}
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, out: TextOutput) {
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, allowSingleValue: Boolean, out: TextOutput) {
beginReadCall("ObjectArray", subtyping, out)
out.comma().append("mapFactory(")
if (componentParser is ObjectValueReader) {

View File

@@ -29,8 +29,14 @@ internal class ObjectValueReader(val type: TypeRef<*>, private val isSubtyping:
out.append(')')
}
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, out: TextOutput) {
beginReadCall("ObjectArray", subtyping, out)
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, allowSingleValue: Boolean, out: TextOutput) {
val readPostfix = if (allowSingleValue) {
"ObjectArrayOrSingleObject"
}
else {
"ObjectArray"
}
beginReadCall(readPostfix, subtyping, out)
writeFactoryArgument(scope, out)
out.append(')')
}

View File

@@ -32,7 +32,7 @@ internal open class PrimitiveValueReader(val className: String, val defaultValue
out.append(className)
}
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, out: TextOutput) {
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, allowSingleValue: Boolean, out: TextOutput) {
if (readPostfix == "String") {
out.append("nextList")
}

View File

@@ -8,7 +8,7 @@ internal class StringIntPairValueReader : ValueReader() {
override fun writeReadCode(scope: ClassScope, subtyping: Boolean, out: TextOutput) {
}
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, out: TextOutput) {
override fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, allowSingleValue: Boolean, out: TextOutput) {
out.append("read").append("IntStringPairs").append('(')
addReaderParameter(subtyping, out)
out.append(')')

View File

@@ -24,7 +24,7 @@ internal abstract class ValueReader {
abstract fun writeReadCode(scope: ClassScope, subtyping: Boolean, out: TextOutput)
open fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, out: TextOutput) {
open fun writeArrayReadCode(scope: ClassScope, subtyping: Boolean, allowSingleValue: Boolean, out: TextOutput) {
throw UnsupportedOperationException()
}