Fix shadowed struct elements

This commit is contained in:
Him188 2020-03-19 14:52:59 +08:00
parent 4c6e427d12
commit 948658c9ec
3 changed files with 81 additions and 69 deletions

View File

@ -181,9 +181,9 @@ internal class JceDecoder(
companion object { companion object {
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
val debuggingMode: Boolean by lazy { false } var debuggingMode: Boolean = false
private var structureHierarchy: Int = 0 var structureHierarchy: Int = 0
inline fun println(value: () -> String) { inline fun println(value: () -> String) {
if (debuggingMode) { if (debuggingMode) {
@ -258,7 +258,10 @@ internal class JceDecoder(
override fun decodeSequentially(): Boolean = false override fun decodeSequentially(): Boolean = false
override fun decodeElementIndex(descriptor: SerialDescriptor): Int { override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
var jceHead = jce.currentHeadOrNull ?: return CompositeDecoder.READ_DONE var jceHead = jce.currentHeadOrNull ?: kotlin.run {
println("decodeElementIndex: currentHead == null")
return CompositeDecoder.READ_DONE
}
println { "decodeElementIndex: ${jce.currentHead}" } println { "decodeElementIndex: ${jce.currentHead}" }
while (!jce.input.endOfInput) { while (!jce.input.endOfInput) {

View File

@ -116,62 +116,69 @@ internal class JceInput(
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
@PublishedApi @PublishedApi
internal fun skipField(type: Byte): Unit = when (type) { internal fun skipField(type: Byte): Unit {
Jce.BYTE -> this.input.discardExact(1) JceDecoder.println { "skipping ${JceHead.findJceTypeName(type)}" }
Jce.SHORT -> this.input.discardExact(2) when (type) {
Jce.INT -> this.input.discardExact(4) Jce.BYTE -> this.input.discardExact(1)
Jce.LONG -> this.input.discardExact(8) Jce.SHORT -> this.input.discardExact(2)
Jce.FLOAT -> this.input.discardExact(4) Jce.INT -> println("readInt=" + this.input.readInt())
Jce.DOUBLE -> this.input.discardExact(8) Jce.LONG -> this.input.discardExact(8)
Jce.STRING1 -> this.input.discardExact(this.input.readUByte().toInt()) Jce.FLOAT -> this.input.discardExact(4)
Jce.STRING4 -> this.input.discardExact(this.input.readInt()) Jce.DOUBLE -> this.input.discardExact(8)
Jce.MAP -> { // map Jce.STRING1 -> this.input.discardExact(this.input.readUByte().toInt())
//println("skip map!") Jce.STRING4 -> this.input.discardExact(this.input.readInt())
nextHead() Jce.MAP -> { // map
repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping map" }) { JceDecoder.structureHierarchy++
readJceIntValue(it) var count: Int = 0
} * 2) { nextHead() // avoid shadowing, don't remove
val currentHead = currentHead repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping map" }) {
prepareNextHead() readJceIntValue(it).also { count = it * 2 }
skipField(currentHead.type) } * 2) {
skipField(currentHead.type)
if (it != count - 1) { // don't read last head
nextHead()
}
}
JceDecoder.structureHierarchy--
} }
} Jce.LIST -> { // list
Jce.LIST -> { // list JceDecoder.structureHierarchy++
JceDecoder.println {"skip list!"} var count: Int = 0
nextHead() nextHead() // avoid shadowing, don't remove
repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping list" }) { repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping list" }) { head ->
readJceIntValue(it) readJceIntValue(head).also { count = it }
}) { }) {
val currentHead = currentHead skipField(currentHead.type)
prepareNextHead() if (it != count - 1) { // don't read last head
skipField(currentHead.type) nextHead()
}
}
JceDecoder.structureHierarchy--
} }
} Jce.STRUCT_BEGIN -> {
Jce.STRUCT_BEGIN -> { JceDecoder.structureHierarchy++
JceDecoder.println {"skip struct!"}
fun skipToStructEnd() {
var head: JceHead var head: JceHead
do { do {
head = nextHead() head = nextHead()
skipField(head.type) skipField(head.type)
} while (head.type != Jce.STRUCT_END) } while (head.type != Jce.STRUCT_END)
JceDecoder.structureHierarchy--
} }
skipToStructEnd() Jce.STRUCT_END, Jce.ZERO_TYPE -> {
} }
Jce.STRUCT_END, Jce.ZERO_TYPE -> { Jce.SIMPLE_LIST -> {
Unit JceDecoder.structureHierarchy++
} var head = nextHead()
Jce.SIMPLE_LIST -> { check(head.type == Jce.BYTE) { "bad simple list element type: " + head.type }
JceDecoder.println { "skip simple list!" } check(head.tag == 0) { "simple list element tag must be 0, but was ${head.tag}" }
var head = nextHead()
check(head.type == Jce.BYTE) { "bad simple list element type: " + head.type }
check(head.tag == 0) { "simple list element tag must be 0, but was ${head.tag}" }
head = nextHead() head = nextHead()
check(head.tag == 0) { "tag for size for simple list must be 0, but was ${head.tag}" } check(head.tag == 0) { "tag for size for simple list must be 0, but was ${head.tag}" }
this.input.discardExact(readJceIntValue(head)) this.input.discardExact(readJceIntValue(head))
JceDecoder.structureHierarchy--
}
else -> error("invalid type: $type")
} }
else -> error("invalid type: $type")
} }
// region readers // region readers

View File

@ -8,10 +8,7 @@ import kotlinx.io.core.writeFully
import kotlinx.serialization.MissingFieldException import kotlinx.serialization.MissingFieldException
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.io.JceStruct import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce import net.mamoe.mirai.qqandroid.io.serialization.jce.*
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceInput
import net.mamoe.mirai.qqandroid.io.serialization.jce.writeJceHead
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
@ -42,6 +39,9 @@ internal const val ZERO_TYPE: Byte = 12
*/ */
@Suppress("INVISIBLE_MEMBER") // bug @Suppress("INVISIBLE_MEMBER") // bug
internal class JceInputTest { internal class JceInputTest {
init {
JceDecoder.debuggingMode = true
}
@Test @Test
fun testIntToStructMap() { fun testIntToStructMap() {
@ -109,31 +109,33 @@ internal class JceInputTest {
val input = buildPacket { val input = buildPacket {
writeJceHead(MAP, 0) // TestSerializableClassB writeJceHead(MAP, 0) // TestSerializableClassB
writeJceHead(BYTE, 0) writeJceHead(BYTE, 0)
writeByte(1) writeByte(1);
writeJceHead(STRUCT_BEGIN, 0);
{ {
writeJceHead(INT, 0) writeJceHead(STRUCT_BEGIN, 0);
writeInt(123) {
writeJceHead(INT, 0)
writeInt(123)
writeJceHead(STRUCT_BEGIN, 123); // TestSerializableClassC writeJceHead(STRUCT_BEGIN, 123); // TestSerializableClassC
{
writeJceHead(INT, 5)
writeInt(123123)
}()
writeJceHead(STRUCT_END, 0)
writeJceHead(INT, 5)
writeInt(9)
}()
writeJceHead(STRUCT_END, 0)
writeJceHead(STRUCT_BEGIN, 1);
{ {
writeJceHead(INT, 5) writeJceHead(INT, 5)
writeInt(123123) writeInt(123123)
}() }()
writeJceHead(STRUCT_END, 0) writeJceHead(STRUCT_END, 0)
writeJceHead(INT, 5)
writeInt(9)
}() }()
writeJceHead(STRUCT_END, 0)
writeJceHead(STRUCT_BEGIN, 1);
{
writeJceHead(INT, 5)
writeInt(123123)
}()
writeJceHead(STRUCT_END, 0)
writeJceHead(STRING1, 1) writeJceHead(STRING1, 1)
writeByte(1) writeByte(1)