Skip to content

JS: mis-compilation in situations where name of property is shadowed #2523

Open
@damianw

Description

@damianw

Description
Certain patterns in a Serializable class will cause code to be compiled incorrectly for JS. While the class will appear to function normally when used from code, instances produced by deserialization will be broken.

To Reproduce
Given a class:

package sample

import kotlinx.serialization.Serializable

@Serializable
data class Model(
    val abc: String?,
) {

    val xyz = abc?.let { xyz -> "$xyz..." }
}

This test will fail when run on JS:

package sample

import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.put
import kotlin.test.Test
import kotlin.test.assertEquals

class FailingTest {

    @Test
    fun fail() {
        val jsonObject = buildJsonObject {
            put("abc", "hello")
        }
        val model = Json.decodeFromJsonElement<Model>(jsonObject)
        assertEquals("hello", model.abc)
        assertEquals("hello...", model.xyz)
    }
}

The test fails at the access of xyz:

AssertionError: Expected <hello...>, actual <undefined...>.
	at DefaultJsAsserter.protoOf.assertTrue_rpw5fg(/.../serialization-bug-sample/build/compileSync/js/test/testDevelopmentExecutable/kotlin/jsMainSources/kotlin/kotlin/test/JsImpl.kt:23)
	at DefaultJsAsserter.assertEquals(/.../serialization-bug-sample/build/compileSync/js/test/testDevelopmentExecutable/kotlin/commonMainSources/kotlin/kotlin/test/Assertions.kt:671)
	at DefaultJsAsserter.protoOf.assertEquals_ldumo(/.../serialization-bug-sample/build/compileSync/js/test/testDevelopmentExecutable/kotlin/jsMainSources/kotlin/kotlin/test/DefaultJsAsserter.kt:27)
	at <global>.assertEquals(/.../serialization-bug-sample/build/compileSync/js/test/testDevelopmentExecutable/kotlin/commonMainSources/kotlin/kotlin/test/Assertions.kt:63)
	at FailingTest.protoOf.fail_x6634e(/.../serialization-bug-sample/src/commonTest/kotlin/sample/FailingTest.kt:32)
	at <global>.fn(kotlin/microgram-serialization-bug-sample-test.js:256)
	at Context.<anonymous>(/.../build/js/packages_imported/kotlin-test-js-runner/src/KotlinTestTeamCityConsoleAdapter.ts:70)
	at <global>.processImmediate(node:internal/timers:466)
Other Eamples

In a lazy:

@Serializable
data class Model(
    val abc: String?,
) {

    val xyz = lazy {
        abc?.let { xyz -> "$xyz..." }
    }
}

This will cause a stack overflow upon access:

RangeError: Maximum call stack size exceeded
	at bitMaskWith (/.../serialization-bug-sample/build/compileSync/js/test/testDevelopmentExecutable/kotlin/js-ir/runtime/BitMask.kt:10:9)
	at implement (/.../serialization-bug-sample/build/compileSync/js/test/testDevelopmentExecutable/kotlin/js-ir/runtime/BitMask.kt:53:45)
	at getInterfaceMaskFor (/.../serialization-bug-sample/build/compileSync/js/test/testDevelopmentExecutable/kotlin/js-ir/runtime/reflectRuntime.kt:39:22)
	at getPropertyCallableRef (/.../serialization-bug-sample/build/compileSync/js/test/testDevelopmentExecutable/kotlin/src/jsMainSources/libraries/stdlib/js/src/kotlin/dynamic.kt:18:56)
	at xyz$factory (kotlin/microgram-serialization-bug-sample.js:254:12)
	at Model.protoOf.get_xyz_18is40_k$ (/.../serialization-bug-sample/build/compileSync/js/test/testDevelopmentExecutable/kotlin/src/kotlin/util/Lazy.kt:4:1)
	at /.../serialization-bug-sample/src/commonMain/kotlin/sample/Model.kt:5:1
	at UnsafeLazyImpl.protoOf.get_value_j01efc_k$ (/.../serialization-bug-sample/build/compileSync/js/test/testDevelopmentExecutable/kotlin/src/kotlin/util/Lazy.kt:81:26)
	at Model.protoOf.get_xyz_18is40_k$ (/.../serialization-bug-sample/build/compileSync/js/test/testDevelopmentExecutable/kotlin/src/kotlin/util/Lazy.kt:44:93)
...

Note the failure for such a test is not actually visible in the output report when running with Gradle, because of:

org.gradle.api.GradleException: Cannot process output: too long teamcity service message (more than 1Mb). Event was lost.
Build failed to prevent inconsistent behaviour. To ignore it use Gradle property 'kotlin.ignore.tcsm.overflow=true'
	at org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessageOutputStreamHandler.overflow(TCServiceMessageOutputStreamHandler.kt:98)
	at org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessageOutputStreamHandler.write(TCServiceMessageOutputStreamHandler.kt:64)
	at org.gradle.process.internal.streams.ExecOutputHandleRunner.writeBuffer(ExecOutputHandleRunner.java:97)
	at org.gradle.process.internal.streams.ExecOutputHandleRunner.lambda$forwardContent$0(ExecOutputHandleRunner.java:81)
	at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:70)
	at org.gradle.process.internal.streams.ExecOutputHandleRunner.forwardContent(ExecOutputHandleRunner.java:80)
	at org.gradle.process.internal.streams.ExecOutputHandleRunner.run(ExecOutputHandleRunner.java:64)
	at CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at 1.run(AbstractManagedExecutor.java:47)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:840)

Expected behavior

The test should pass. More specifically, the reference to the local parameter xyz should not refer to the class's xyz property in the compiled code. When the local parameter name xyz is renamed to something else, the test passes as expected.

Environment

  • Kotlin version: 1.9.21
  • Library version: 1.6.2
  • Kotlin platforms: JS
  • Gradle version: 8.3

Rudimentary Analysis

This is the compiled code for the broken case (original example at the top):

Compiled Code
(function (root, factory) {
  if (typeof define === 'function' && define.amd)
    define(['exports', './kotlin-kotlin-stdlib.js', './kotlinx-serialization-kotlinx-serialization-core.js'], factory);
  else if (typeof exports === 'object')
    factory(module.exports, require('./kotlin-kotlin-stdlib.js'), require('./kotlinx-serialization-kotlinx-serialization-core.js'));
  else {
    if (typeof this['kotlin-kotlin-stdlib'] === 'undefined') {
      throw new Error("Error loading module 'com.sample:serialization-bug-sample'. Its dependency 'kotlin-kotlin-stdlib' was not found. Please, check whether 'kotlin-kotlin-stdlib' is loaded prior to 'com.sample:serialization-bug-sample'.");
    }
    if (typeof this['kotlinx-serialization-kotlinx-serialization-core'] === 'undefined') {
      throw new Error("Error loading module 'com.sample:serialization-bug-sample'. Its dependency 'kotlinx-serialization-kotlinx-serialization-core' was not found. Please, check whether 'kotlinx-serialization-kotlinx-serialization-core' is loaded prior to 'com.sample:serialization-bug-sample'.");
    }
    root['com.sample:serialization-bug-sample'] = factory(typeof this['com.sample:serialization-bug-sample'] === 'undefined' ? {} : this['com.sample:serialization-bug-sample'], this['kotlin-kotlin-stdlib'], this['kotlinx-serialization-kotlinx-serialization-core']);
  }
}(this, function (_, kotlin_kotlin, kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core) {
  'use strict';
  //region block: imports
  var protoOf = kotlin_kotlin.$_$.x5;
  var objectMeta = kotlin_kotlin.$_$.w5;
  var setMetadataFor = kotlin_kotlin.$_$.y5;
  var PluginGeneratedSerialDescriptor = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.l;
  var StringSerializer_getInstance = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.b;
  var get_nullable = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.c;
  var UnknownFieldException_init_$Create$ = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.a;
  var THROW_CCE = kotlin_kotlin.$_$.q6;
  var typeParametersSerializers = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.i;
  var GeneratedSerializer = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.j;
  var VOID = kotlin_kotlin.$_$.d;
  var throwMissingFieldException = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.m;
  var objectCreate = kotlin_kotlin.$_$.v5;
  var getStringHashCode = kotlin_kotlin.$_$.f5;
  var classMeta = kotlin_kotlin.$_$.b5;
  //endregion
  //region block: pre-declaration
  setMetadataFor(Companion, 'Companion', objectMeta);
  setMetadataFor($serializer, '$serializer', objectMeta, VOID, [GeneratedSerializer]);
  setMetadataFor(Model, 'Model', classMeta, VOID, VOID, VOID, VOID, {0: $serializer_getInstance});
  //endregion
  function Companion() {
    Companion_instance = this;
  }
  protoOf(Companion).serializer_9w0wvi_k$ = function () {
    return $serializer_getInstance();
  };
  var Companion_instance;
  function Companion_getInstance() {
    if (Companion_instance == null)
      new Companion();
    return Companion_instance;
  }
  function $serializer() {
    $serializer_instance = this;
    var tmp0_serialDesc = new PluginGeneratedSerialDescriptor('sample.Model', this, 2);
    tmp0_serialDesc.addElement_5pzumi_k$('abc', false);
    tmp0_serialDesc.addElement_5pzumi_k$('xyz', true);
    this.descriptor_1 = tmp0_serialDesc;
  }
  protoOf($serializer).get_descriptor_wjt6a0_k$ = function () {
    return this.descriptor_1;
  };
  protoOf($serializer).childSerializers_5ghqw5_k$ = function () {
    // Inline function 'kotlin.arrayOf' call
    // Inline function 'kotlin.js.unsafeCast' call
    // Inline function 'kotlin.js.asDynamic' call
    return [get_nullable(StringSerializer_getInstance()), get_nullable(StringSerializer_getInstance())];
  };
  protoOf($serializer).deserialize_sy6x50_k$ = function (decoder) {
    var tmp0_desc = this.descriptor_1;
    var tmp1_flag = true;
    var tmp2_index = 0;
    var tmp3_bitMask0 = 0;
    var tmp4_local0 = null;
    var tmp5_local1 = null;
    var tmp6_input = decoder.beginStructure_yljocp_k$(tmp0_desc);
    if (tmp6_input.decodeSequentially_xlblqy_k$()) {
      tmp4_local0 = tmp6_input.decodeNullableSerializableElement_k2y6ab_k$(tmp0_desc, 0, StringSerializer_getInstance(), tmp4_local0);
      tmp3_bitMask0 = tmp3_bitMask0 | 1;
      tmp5_local1 = tmp6_input.decodeNullableSerializableElement_k2y6ab_k$(tmp0_desc, 1, StringSerializer_getInstance(), tmp5_local1);
      tmp3_bitMask0 = tmp3_bitMask0 | 2;
    } else
      while (tmp1_flag) {
        tmp2_index = tmp6_input.decodeElementIndex_bstkhp_k$(tmp0_desc);
        switch (tmp2_index) {
          case -1:
            tmp1_flag = false;
            break;
          case 0:
            tmp4_local0 = tmp6_input.decodeNullableSerializableElement_k2y6ab_k$(tmp0_desc, 0, StringSerializer_getInstance(), tmp4_local0);
            tmp3_bitMask0 = tmp3_bitMask0 | 1;
            break;
          case 1:
            tmp5_local1 = tmp6_input.decodeNullableSerializableElement_k2y6ab_k$(tmp0_desc, 1, StringSerializer_getInstance(), tmp5_local1);
            tmp3_bitMask0 = tmp3_bitMask0 | 2;
            break;
          default:
            throw UnknownFieldException_init_$Create$(tmp2_index);
        }
      }
    tmp6_input.endStructure_1xqz0n_k$(tmp0_desc);
    return Model_init_$Create$(tmp3_bitMask0, tmp4_local0, tmp5_local1, null);
  };
  protoOf($serializer).serialize_n60lb7_k$ = function (encoder, value) {
    var tmp0_desc = this.descriptor_1;
    var tmp1_output = encoder.beginStructure_yljocp_k$(tmp0_desc);
    tmp1_output.encodeNullableSerializableElement_5lquiv_k$(tmp0_desc, 0, StringSerializer_getInstance(), value.abc_1);
    var tmp;
    if (tmp1_output.shouldEncodeElementDefault_x8eyid_k$(tmp0_desc, 1)) {
      tmp = true;
    } else {
      var tmp_0;
      if (value.abc_1 == null) {
        tmp_0 = null;
      } else {
        // Inline function 'kotlin.let' call
        // Inline function 'kotlin.contracts.contract' call
        // Inline function 'sample.$serializer.serialize.<anonymous>' call
        tmp_0 = '' + value.xyz_1 + '...';
      }
      tmp = !(value.xyz_1 == tmp_0);
    }
    if (tmp) {
      tmp1_output.encodeNullableSerializableElement_5lquiv_k$(tmp0_desc, 1, StringSerializer_getInstance(), value.xyz_1);
    }
    tmp1_output.endStructure_1xqz0n_k$(tmp0_desc);
  };
  protoOf($serializer).serialize_5ase3y_k$ = function (encoder, value) {
    return this.serialize_n60lb7_k$(encoder, value instanceof Model ? value : THROW_CCE());
  };
  var $serializer_instance;
  function $serializer_getInstance() {
    if ($serializer_instance == null)
      new $serializer();
    return $serializer_instance;
  }
  function Model_init_$Init$(seen1, abc, xyz, serializationConstructorMarker, $this) {
    if (!(1 === (1 & seen1))) {
      throwMissingFieldException(seen1, 1, $serializer_getInstance().descriptor_1);
    }
    $this.abc_1 = abc;
    if (0 === (seen1 & 2)) {
      var tmp = $this;
      var tmp_0;
      if ($this.abc_1 == null) {
        tmp_0 = null;
      } else {
        // Inline function 'kotlin.let' call
        // Inline function 'kotlin.contracts.contract' call
        // Inline function 'sample.Model.<init>.<anonymous>' call
        tmp_0 = '' + $this.xyz_1 + '...';
      }
      tmp.xyz_1 = tmp_0;
    } else
      $this.xyz_1 = xyz;
    return $this;
  }
  function Model_init_$Create$(seen1, abc, xyz, serializationConstructorMarker) {
    return Model_init_$Init$(seen1, abc, xyz, serializationConstructorMarker, objectCreate(protoOf(Model)));
  }
  function Model(abc) {
    Companion_getInstance();
    this.abc_1 = abc;
    var tmp = this;
    var tmp0_safe_receiver = this.abc_1;
    var tmp_0;
    if (tmp0_safe_receiver == null) {
      tmp_0 = null;
    } else {
      // Inline function 'kotlin.let' call
      // Inline function 'kotlin.contracts.contract' call
      // Inline function 'sample.Model.xyz.<anonymous>' call
      tmp_0 = tmp0_safe_receiver + '...';
    }
    tmp.xyz_1 = tmp_0;
  }
  protoOf(Model).get_abc_18j9qf_k$ = function () {
    return this.abc_1;
  };
  protoOf(Model).get_xyz_18is40_k$ = function () {
    return this.xyz_1;
  };
  protoOf(Model).component1_7eebsc_k$ = function () {
    return this.abc_1;
  };
  protoOf(Model).copy_q202ni_k$ = function (abc) {
    return new Model(abc);
  };
  protoOf(Model).copy$default_1m1bzh_k$ = function (abc, $super) {
    abc = abc === VOID ? this.abc_1 : abc;
    return $super === VOID ? this.copy_q202ni_k$(abc) : $super.copy_q202ni_k$.call(this, abc);
  };
  protoOf(Model).toString = function () {
    return 'Model(abc=' + this.abc_1 + ')';
  };
  protoOf(Model).hashCode = function () {
    return this.abc_1 == null ? 0 : getStringHashCode(this.abc_1);
  };
  protoOf(Model).equals = function (other) {
    if (this === other)
      return true;
    if (!(other instanceof Model))
      return false;
    var tmp0_other_with_cast = other instanceof Model ? other : THROW_CCE();
    if (!(this.abc_1 == tmp0_other_with_cast.abc_1))
      return false;
    return true;
  };
  //region block: post-declaration
  protoOf($serializer).typeParametersSerializers_fr94fx_k$ = typeParametersSerializers;
  //endregion
  return _;
}));

//# sourceMappingURL=serialization-bug-sample.js.map

When the xyz parameter name is replaced by another name, e.g. other:

@Serializable
data class Model(
    val abc: String?,
) {

    val xyz = abc?.let { other -> "$other..." }
}

Then we get the following compiled code:

Compiled Code
(function (root, factory) {
  if (typeof define === 'function' && define.amd)
    define(['exports', './kotlin-kotlin-stdlib.js', './kotlinx-serialization-kotlinx-serialization-core.js'], factory);
  else if (typeof exports === 'object')
    factory(module.exports, require('./kotlin-kotlin-stdlib.js'), require('./kotlinx-serialization-kotlinx-serialization-core.js'));
  else {
    if (typeof this['kotlin-kotlin-stdlib'] === 'undefined') {
      throw new Error("Error loading module 'com.sample:serialization-bug-sample'. Its dependency 'kotlin-kotlin-stdlib' was not found. Please, check whether 'kotlin-kotlin-stdlib' is loaded prior to 'com.sample:serialization-bug-sample'.");
    }
    if (typeof this['kotlinx-serialization-kotlinx-serialization-core'] === 'undefined') {
      throw new Error("Error loading module 'com.sample:serialization-bug-sample'. Its dependency 'kotlinx-serialization-kotlinx-serialization-core' was not found. Please, check whether 'kotlinx-serialization-kotlinx-serialization-core' is loaded prior to 'com.sample:serialization-bug-sample'.");
    }
    root['com.sample:serialization-bug-sample'] = factory(typeof this['com.sample:serialization-bug-sample'] === 'undefined' ? {} : this['com.sample:serialization-bug-sample'], this['kotlin-kotlin-stdlib'], this['kotlinx-serialization-kotlinx-serialization-core']);
  }
}(this, function (_, kotlin_kotlin, kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core) {
  'use strict';
  //region block: imports
  var protoOf = kotlin_kotlin.$_$.x5;
  var objectMeta = kotlin_kotlin.$_$.w5;
  var setMetadataFor = kotlin_kotlin.$_$.y5;
  var PluginGeneratedSerialDescriptor = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.l;
  var StringSerializer_getInstance = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.b;
  var get_nullable = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.c;
  var UnknownFieldException_init_$Create$ = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.a;
  var THROW_CCE = kotlin_kotlin.$_$.q6;
  var typeParametersSerializers = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.i;
  var GeneratedSerializer = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.j;
  var VOID = kotlin_kotlin.$_$.d;
  var throwMissingFieldException = kotlin_org_jetbrains_kotlinx_kotlinx_serialization_core.$_$.m;
  var objectCreate = kotlin_kotlin.$_$.v5;
  var getStringHashCode = kotlin_kotlin.$_$.f5;
  var classMeta = kotlin_kotlin.$_$.b5;
  //endregion
  //region block: pre-declaration
  setMetadataFor(Companion, 'Companion', objectMeta);
  setMetadataFor($serializer, '$serializer', objectMeta, VOID, [GeneratedSerializer]);
  setMetadataFor(Model, 'Model', classMeta, VOID, VOID, VOID, VOID, {0: $serializer_getInstance});
  //endregion
  function Companion() {
    Companion_instance = this;
  }
  protoOf(Companion).serializer_9w0wvi_k$ = function () {
    return $serializer_getInstance();
  };
  var Companion_instance;
  function Companion_getInstance() {
    if (Companion_instance == null)
      new Companion();
    return Companion_instance;
  }
  function $serializer() {
    $serializer_instance = this;
    var tmp0_serialDesc = new PluginGeneratedSerialDescriptor('sample.Model', this, 2);
    tmp0_serialDesc.addElement_5pzumi_k$('abc', false);
    tmp0_serialDesc.addElement_5pzumi_k$('xyz', true);
    this.descriptor_1 = tmp0_serialDesc;
  }
  protoOf($serializer).get_descriptor_wjt6a0_k$ = function () {
    return this.descriptor_1;
  };
  protoOf($serializer).childSerializers_5ghqw5_k$ = function () {
    // Inline function 'kotlin.arrayOf' call
    // Inline function 'kotlin.js.unsafeCast' call
    // Inline function 'kotlin.js.asDynamic' call
    return [get_nullable(StringSerializer_getInstance()), get_nullable(StringSerializer_getInstance())];
  };
  protoOf($serializer).deserialize_sy6x50_k$ = function (decoder) {
    var tmp0_desc = this.descriptor_1;
    var tmp1_flag = true;
    var tmp2_index = 0;
    var tmp3_bitMask0 = 0;
    var tmp4_local0 = null;
    var tmp5_local1 = null;
    var tmp6_input = decoder.beginStructure_yljocp_k$(tmp0_desc);
    if (tmp6_input.decodeSequentially_xlblqy_k$()) {
      tmp4_local0 = tmp6_input.decodeNullableSerializableElement_k2y6ab_k$(tmp0_desc, 0, StringSerializer_getInstance(), tmp4_local0);
      tmp3_bitMask0 = tmp3_bitMask0 | 1;
      tmp5_local1 = tmp6_input.decodeNullableSerializableElement_k2y6ab_k$(tmp0_desc, 1, StringSerializer_getInstance(), tmp5_local1);
      tmp3_bitMask0 = tmp3_bitMask0 | 2;
    } else
      while (tmp1_flag) {
        tmp2_index = tmp6_input.decodeElementIndex_bstkhp_k$(tmp0_desc);
        switch (tmp2_index) {
          case -1:
            tmp1_flag = false;
            break;
          case 0:
            tmp4_local0 = tmp6_input.decodeNullableSerializableElement_k2y6ab_k$(tmp0_desc, 0, StringSerializer_getInstance(), tmp4_local0);
            tmp3_bitMask0 = tmp3_bitMask0 | 1;
            break;
          case 1:
            tmp5_local1 = tmp6_input.decodeNullableSerializableElement_k2y6ab_k$(tmp0_desc, 1, StringSerializer_getInstance(), tmp5_local1);
            tmp3_bitMask0 = tmp3_bitMask0 | 2;
            break;
          default:
            throw UnknownFieldException_init_$Create$(tmp2_index);
        }
      }
    tmp6_input.endStructure_1xqz0n_k$(tmp0_desc);
    return Model_init_$Create$(tmp3_bitMask0, tmp4_local0, tmp5_local1, null);
  };
  protoOf($serializer).serialize_n60lb7_k$ = function (encoder, value) {
    var tmp0_desc = this.descriptor_1;
    var tmp1_output = encoder.beginStructure_yljocp_k$(tmp0_desc);
    tmp1_output.encodeNullableSerializableElement_5lquiv_k$(tmp0_desc, 0, StringSerializer_getInstance(), value.abc_1);
    var tmp;
    if (tmp1_output.shouldEncodeElementDefault_x8eyid_k$(tmp0_desc, 1)) {
      tmp = true;
    } else {
      var tmp0_safe_receiver = value.abc_1;
      var tmp_0;
      if (tmp0_safe_receiver == null) {
        tmp_0 = null;
      } else {
        // Inline function 'kotlin.let' call
        // Inline function 'kotlin.contracts.contract' call
        // Inline function 'sample.$serializer.serialize.<anonymous>' call
        tmp_0 = tmp0_safe_receiver + '...';
      }
      tmp = !(value.xyz_1 == tmp_0);
    }
    if (tmp) {
      tmp1_output.encodeNullableSerializableElement_5lquiv_k$(tmp0_desc, 1, StringSerializer_getInstance(), value.xyz_1);
    }
    tmp1_output.endStructure_1xqz0n_k$(tmp0_desc);
  };
  protoOf($serializer).serialize_5ase3y_k$ = function (encoder, value) {
    return this.serialize_n60lb7_k$(encoder, value instanceof Model ? value : THROW_CCE());
  };
  var $serializer_instance;
  function $serializer_getInstance() {
    if ($serializer_instance == null)
      new $serializer();
    return $serializer_instance;
  }
  function Model_init_$Init$(seen1, abc, xyz, serializationConstructorMarker, $this) {
    if (!(1 === (1 & seen1))) {
      throwMissingFieldException(seen1, 1, $serializer_getInstance().descriptor_1);
    }
    $this.abc_1 = abc;
    if (0 === (seen1 & 2)) {
      var tmp = $this;
      var tmp0_safe_receiver = $this.abc_1;
      var tmp_0;
      if (tmp0_safe_receiver == null) {
        tmp_0 = null;
      } else {
        // Inline function 'kotlin.let' call
        // Inline function 'kotlin.contracts.contract' call
        // Inline function 'sample.Model.<init>.<anonymous>' call
        tmp_0 = tmp0_safe_receiver + '...';
      }
      tmp.xyz_1 = tmp_0;
    } else
      $this.xyz_1 = xyz;
    return $this;
  }
  function Model_init_$Create$(seen1, abc, xyz, serializationConstructorMarker) {
    return Model_init_$Init$(seen1, abc, xyz, serializationConstructorMarker, objectCreate(protoOf(Model)));
  }
  function Model(abc) {
    Companion_getInstance();
    this.abc_1 = abc;
    var tmp = this;
    var tmp0_safe_receiver = this.abc_1;
    var tmp_0;
    if (tmp0_safe_receiver == null) {
      tmp_0 = null;
    } else {
      // Inline function 'kotlin.let' call
      // Inline function 'kotlin.contracts.contract' call
      // Inline function 'sample.Model.xyz.<anonymous>' call
      tmp_0 = tmp0_safe_receiver + '...';
    }
    tmp.xyz_1 = tmp_0;
  }
  protoOf(Model).get_abc_18j9qf_k$ = function () {
    return this.abc_1;
  };
  protoOf(Model).get_xyz_18is40_k$ = function () {
    return this.xyz_1;
  };
  protoOf(Model).component1_7eebsc_k$ = function () {
    return this.abc_1;
  };
  protoOf(Model).copy_q202ni_k$ = function (abc) {
    return new Model(abc);
  };
  protoOf(Model).copy$default_1m1bzh_k$ = function (abc, $super) {
    abc = abc === VOID ? this.abc_1 : abc;
    return $super === VOID ? this.copy_q202ni_k$(abc) : $super.copy_q202ni_k$.call(this, abc);
  };
  protoOf(Model).toString = function () {
    return 'Model(abc=' + this.abc_1 + ')';
  };
  protoOf(Model).hashCode = function () {
    return this.abc_1 == null ? 0 : getStringHashCode(this.abc_1);
  };
  protoOf(Model).equals = function (other) {
    if (this === other)
      return true;
    if (!(other instanceof Model))
      return false;
    var tmp0_other_with_cast = other instanceof Model ? other : THROW_CCE();
    if (!(this.abc_1 == tmp0_other_with_cast.abc_1))
      return false;
    return true;
  };
  //region block: post-declaration
  protoOf($serializer).typeParametersSerializers_fr94fx_k$ = typeParametersSerializers;
  //endregion
  return _;
}));

//# sourceMappingURL=serialization-bug-sample.js.map

The diff between these two is:

110d109
<       var tmp0_safe_receiver = value.abc_1;
112c111
<       if (tmp0_safe_receiver == null) {
---
>       if (value.abc_1 == null) {
118c117
<         tmp_0 = tmp0_safe_receiver + '...';
---
>         tmp_0 = '' + value.xyz_1 + '...';
143d141
<       var tmp0_safe_receiver = $this.abc_1;
145c143
<       if (tmp0_safe_receiver == null) {
---
>       if ($this.abc_1 == null) {
151c149
<         tmp_0 = tmp0_safe_receiver + '...';
---
>         tmp_0 = '' + $this.xyz_1 + '...';

It is evident that the compiler plugin mistakes the name xyz in the scope of the let lambda to refer to the xyz property of the class, instead of by the local variable.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions