diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2020-07-22 23:24:22 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2020-07-22 23:24:22 +0000 |
commit | 1413f21a5e2541f5d33e68a789d2aa9f00a5cb1c (patch) | |
tree | c3406b3d5df4071cffd5eb0a44fe067d9def1e3a | |
parent | 41bafb48a374225d9e24299ed3d0970f6ff672f3 (diff) | |
parent | b50f04dff030e0f3efc8374d342c5efeeade710f (diff) | |
download | art-android11-qpr1-d-release.tar.gz |
Snap for 6701292 from b50f04dff030e0f3efc8374d342c5efeeade710f to rvc-qpr1-releaseandroid-11.0.0_r31android-11.0.0_r29android-11.0.0_r28android-11.0.0_r27android-11.0.0_r26android-11.0.0_r24android-11.0.0_r23android-11.0.0_r22android-11.0.0_r21android-11.0.0_r20android-11.0.0_r19android-11.0.0_r18android11-qpr1-s2-releaseandroid11-qpr1-s1-releaseandroid11-qpr1-releaseandroid11-qpr1-d-s1-releaseandroid11-qpr1-d-release
Change-Id: I50e71dade77dd2930ca2e6b40a62a82b5ea2005c
-rw-r--r-- | openjdkjvmti/ti_redefine.cc | 97 | ||||
-rw-r--r-- | test/2036-structural-subclass-shadow/expected.txt | 8 | ||||
-rw-r--r-- | test/2036-structural-subclass-shadow/info.txt | 5 | ||||
-rwxr-xr-x | test/2036-structural-subclass-shadow/run | 17 | ||||
-rw-r--r-- | test/2036-structural-subclass-shadow/src-art/Main.java | 21 | ||||
l--------- | test/2036-structural-subclass-shadow/src-art/art/Redefinition.java | 1 | ||||
-rw-r--r-- | test/2036-structural-subclass-shadow/src-art/art/Test2036.java | 136 | ||||
-rw-r--r-- | test/2036-structural-subclass-shadow/src/Main.java | 21 | ||||
-rw-r--r-- | test/knownfailures.json | 3 |
9 files changed, 271 insertions, 38 deletions
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index c457dba95e..049e0cfbf3 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -2761,8 +2761,6 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat art::Locks::mutator_lock_->AssertExclusiveHeld(driver_->self_); art::ClassLinker* cl = driver_->runtime_->GetClassLinker(); art::ScopedAssertNoThreadSuspension sants(__FUNCTION__); - art::ObjPtr<art::mirror::Class> orig(holder.GetMirrorClass()); - art::ObjPtr<art::mirror::Class> replacement(holder.GetNewClassObject()); art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> new_classes(holder.GetNewClasses()); art::ObjPtr<art::mirror::ObjectArray<art::mirror::Class>> old_classes(holder.GetOldClasses()); // Collect mappings from old to new fields/methods @@ -2773,8 +2771,6 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat holder.GetNewInstanceObjects()); art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> old_instances( holder.GetOldInstanceObjects()); - CHECK(!orig.IsNull()); - CHECK(!replacement.IsNull()); // Once we do the ReplaceReferences old_classes will have the new_classes in it. We want to keep // ahold of the old classes so copy them now. std::vector<art::ObjPtr<art::mirror::Class>> old_classes_vec(old_classes->Iterate().begin(), @@ -2848,16 +2844,26 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat } } // We can only shadow things from our superclasses - if (LIKELY(!field_or_method->GetDeclaringClass()->IsAssignableFrom(orig))) { + auto orig_classes_iter = old_classes->Iterate(); + auto replacement_classes_iter = new_classes->Iterate(); + art::ObjPtr<art::mirror::Class> f_or_m_class = field_or_method->GetDeclaringClass(); + if (LIKELY(!f_or_m_class->IsAssignableFrom(holder.GetMirrorClass()) && + std::find(orig_classes_iter.begin(), orig_classes_iter.end(), f_or_m_class) == + orig_classes_iter.end())) { return false; } if constexpr (is_method) { - auto direct_methods = replacement->GetDirectMethods(art::kRuntimePointerSize); - return std::find_if(direct_methods.begin(), - direct_methods.end(), - [&](art::ArtMethod& m) REQUIRES(art::Locks::mutator_lock_) { - return UNLIKELY(m.HasSameNameAndSignature(field_or_method)); - }) != direct_methods.end(); + return std::any_of( + replacement_classes_iter.begin(), + replacement_classes_iter.end(), + [&](art::ObjPtr<art::mirror::Class> cand) REQUIRES(art::Locks::mutator_lock_) { + auto direct_methods = cand->GetDirectMethods(art::kRuntimePointerSize); + return std::find_if(direct_methods.begin(), + direct_methods.end(), + [&](art::ArtMethod& m) REQUIRES(art::Locks::mutator_lock_) { + return UNLIKELY(m.HasSameNameAndSignature(field_or_method)); + }) != direct_methods.end(); + }); } else { auto pred = [&](art::ArtField& f) REQUIRES(art::Locks::mutator_lock_) { return std::string_view(f.GetName()) == std::string_view(field_or_method->GetName()) && @@ -2865,11 +2871,21 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat std::string_view(field_or_method->GetTypeDescriptor()); }; if (field_or_method->IsStatic()) { - auto sfields = replacement->GetSFields(); - return std::find_if(sfields.begin(), sfields.end(), pred) != sfields.end(); + return std::any_of( + replacement_classes_iter.begin(), + replacement_classes_iter.end(), + [&](art::ObjPtr<art::mirror::Class> cand) REQUIRES(art::Locks::mutator_lock_) { + auto sfields = cand->GetSFields(); + return std::find_if(sfields.begin(), sfields.end(), pred) != sfields.end(); + }); } else { - auto ifields = replacement->GetIFields(); - return std::find_if(ifields.begin(), ifields.end(), pred) != ifields.end(); + return std::any_of( + replacement_classes_iter.begin(), + replacement_classes_iter.end(), + [&](art::ObjPtr<art::mirror::Class> cand) REQUIRES(art::Locks::mutator_lock_) { + auto ifields = cand->GetIFields(); + return std::find_if(ifields.begin(), ifields.end(), pred) != ifields.end(); + }); } } }; @@ -2879,28 +2895,28 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat [&](art::ArtField* f, const auto& info) REQUIRES(art::Locks::mutator_lock_) { DCHECK(f != nullptr) << info; auto it = field_map.find(f); - if (it != field_map.end()) { + if (UNLIKELY(could_change_resolution_of(f, info))) { + // Dex-cache Resolution might change. Just clear the resolved value. + VLOG(plugin) << "Clearing resolution " << info << " for (field) " << f->PrettyField(); + return static_cast<art::ArtField*>(nullptr); + } else if (it != field_map.end()) { VLOG(plugin) << "Updating " << info << " object for (field) " << it->second->PrettyField(); return it->second; - } else if (UNLIKELY(could_change_resolution_of(f, info))) { - // Resolution might change. Just clear the resolved value. - VLOG(plugin) << "Clearing resolution " << info << " for (field) " << f->PrettyField(); - return static_cast<art::ArtField*>(nullptr); } return f; }, [&](art::ArtMethod* m, const auto& info) REQUIRES(art::Locks::mutator_lock_) { DCHECK(m != nullptr) << info; auto it = method_map.find(m); - if (it != method_map.end()) { + if (UNLIKELY(could_change_resolution_of(m, info))) { + // Dex-cache Resolution might change. Just clear the resolved value. + VLOG(plugin) << "Clearing resolution " << info << " for (method) " << m->PrettyMethod(); + return static_cast<art::ArtMethod*>(nullptr); + } else if (it != method_map.end()) { VLOG(plugin) << "Updating " << info << " object for (method) " << it->second->PrettyMethod(); return it->second; - } else if (UNLIKELY(could_change_resolution_of(m, info))) { - // Resolution might change. Just clear the resolved value. - VLOG(plugin) << "Clearing resolution " << info << " for (method) " << m->PrettyMethod(); - return static_cast<art::ArtMethod*>(nullptr); } return m; }); @@ -2955,18 +2971,25 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat if (art::kIsDebugBuild) { // Just make sure we didn't screw up any of the now obsolete methods or fields. We need their // declaring-class to still be the obolete class - orig->VisitMethods([&](art::ArtMethod* method) REQUIRES_SHARED(art::Locks::mutator_lock_) { - if (method->IsCopied()) { - // Copied methods have interfaces as their declaring class. - return; - } - DCHECK_EQ(method->GetDeclaringClass(), orig) << method->GetDeclaringClass()->PrettyClass() - << " vs " << orig->PrettyClass(); - }, art::kRuntimePointerSize); - orig->VisitFields([&](art::ArtField* field) REQUIRES_SHARED(art::Locks::mutator_lock_) { - DCHECK_EQ(field->GetDeclaringClass(), orig) << field->GetDeclaringClass()->PrettyClass() - << " vs " << orig->PrettyClass(); - }); + std::for_each( + old_classes_vec.cbegin(), + old_classes_vec.cend(), + [](art::ObjPtr<art::mirror::Class> orig) REQUIRES_SHARED(art::Locks::mutator_lock_) { + orig->VisitMethods( + [&](art::ArtMethod* method) REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (method->IsCopied()) { + // Copied methods have interfaces as their declaring class. + return; + } + DCHECK_EQ(method->GetDeclaringClass(), orig) + << method->GetDeclaringClass()->PrettyClass() << " vs " << orig->PrettyClass(); + }, + art::kRuntimePointerSize); + orig->VisitFields([&](art::ArtField* field) REQUIRES_SHARED(art::Locks::mutator_lock_) { + DCHECK_EQ(field->GetDeclaringClass(), orig) + << field->GetDeclaringClass()->PrettyClass() << " vs " << orig->PrettyClass(); + }); + }); } } diff --git a/test/2036-structural-subclass-shadow/expected.txt b/test/2036-structural-subclass-shadow/expected.txt new file mode 100644 index 0000000000..7fb86e3dce --- /dev/null +++ b/test/2036-structural-subclass-shadow/expected.txt @@ -0,0 +1,8 @@ +value-getter is 42 +value is 42 +static-call is 1337 +static-value is 3.14 +value-getter is 0 +value is 0 +static-call is -559038737 +static-value is 0.0 diff --git a/test/2036-structural-subclass-shadow/info.txt b/test/2036-structural-subclass-shadow/info.txt new file mode 100644 index 0000000000..317dbf82f4 --- /dev/null +++ b/test/2036-structural-subclass-shadow/info.txt @@ -0,0 +1,5 @@ +Tests dex-cache invalidation with structural redefinition. + +Regression test for b/161846143. Structural redefinition was incorrectly +failing to invalidate some dex-cache entries causing incorrect field/method +resolution in limited cases. diff --git a/test/2036-structural-subclass-shadow/run b/test/2036-structural-subclass-shadow/run new file mode 100755 index 0000000000..ff387ff80e --- /dev/null +++ b/test/2036-structural-subclass-shadow/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true diff --git a/test/2036-structural-subclass-shadow/src-art/Main.java b/test/2036-structural-subclass-shadow/src-art/Main.java new file mode 100644 index 0000000000..d707701ae5 --- /dev/null +++ b/test/2036-structural-subclass-shadow/src-art/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + art.Test2036.run(); + } +} diff --git a/test/2036-structural-subclass-shadow/src-art/art/Redefinition.java b/test/2036-structural-subclass-shadow/src-art/art/Redefinition.java new file mode 120000 index 0000000000..81eaf31bbb --- /dev/null +++ b/test/2036-structural-subclass-shadow/src-art/art/Redefinition.java @@ -0,0 +1 @@ +../../../jvmti-common/Redefinition.java
\ No newline at end of file diff --git a/test/2036-structural-subclass-shadow/src-art/art/Test2036.java b/test/2036-structural-subclass-shadow/src-art/art/Test2036.java new file mode 100644 index 0000000000..8ca8e2a613 --- /dev/null +++ b/test/2036-structural-subclass-shadow/src-art/art/Test2036.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import java.util.Base64; + +public class Test2036 { + public static class Transform { + public Transform() {} + } + + public static class SubTransform extends Transform { + public SubTransform() {} + + public static int FooBar() { + return 1337; + } + + public int foo = 42; + public static double STATIC_FOO = 3.14d; + } + + public static class SubSubTransform extends SubTransform { + public SubSubTransform() {} + } + + public static class SubSubSubTransform extends SubSubTransform { + public SubSubSubTransform() {} + + public int getFoo() { + return this.foo; + } + } + /* + * base64 encoded class/dex file for + * Base64 generated using: + * % javac Test2036.java + * % d8 Test2035\$Transform.class + * % base64 classes.dex| sed 's:^:":' | sed 's:$:" +:' + * + * package art; + * public static class Transform { + * public Transform() {} + * public int bar; + * } + */ + private static final byte[] DEX_BYTES = + Base64.getDecoder() + .decode( + "ZGV4CjAzNQAZlSz5hewOnpRnplr0AnEqh8+AKNc2qhp0AwAAcAAAAHhWNBIAAAAAAAAAALwCAAAP" + + "AAAAcAAAAAcAAACsAAAAAQAAAMgAAAABAAAA1AAAAAIAAADcAAAAAQAAAOwAAABoAgAADAEAACgB" + + "AAAwAQAAMwEAAE0BAABdAQAAgQEAAKEBAAC1AQAAxAEAAM8BAADSAQAA3wEAAOQBAADqAQAA8QEA" + + "AAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAkAAAAJAAAABgAAAAAAAAABAAAACwAAAAEAAAAAAAAA" + + "BQAAAAAAAAABAAAAAQAAAAUAAAAAAAAABwAAAKwCAACOAgAAAAAAAAEAAQABAAAAJAEAAAQAAABw" + + "EAEAAAAOABgADgAGPGluaXQ+AAFJABhMYXJ0L1Rlc3QyMDM2JFRyYW5zZm9ybTsADkxhcnQvVGVz" + + "dDIwMzY7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90" + + "YXRpb24vSW5uZXJDbGFzczsAEkxqYXZhL2xhbmcvT2JqZWN0OwANVGVzdDIwMzYuamF2YQAJVHJh" + + "bnNmb3JtAAFWAAthY2Nlc3NGbGFncwADYmFyAARuYW1lAAV2YWx1ZQCLAX5+RDh7ImNvbXBpbGF0" + + "aW9uLW1vZGUiOiJkZWJ1ZyIsImhhcy1jaGVja3N1bXMiOmZhbHNlLCJtaW4tYXBpIjoxLCJzaGEt" + + "MSI6IjJkMDE0MjEyNjVlMjUxMWM5MGUxZTY2NTU0NWEzMzliM2M5OWMwYWYiLCJ2ZXJzaW9uIjoi" + + "Mi4yLjMtZGV2In0AAgMBDRgCAgQCCgQJDBcIAAEBAAABAIGABIwCAAAAAAAAAgAAAH8CAACFAgAA" + + "oAIAAAAAAAAAAAAAAAAAAA8AAAAAAAAAAQAAAAAAAAABAAAADwAAAHAAAAACAAAABwAAAKwAAAAD" + + "AAAAAQAAAMgAAAAEAAAAAQAAANQAAAAFAAAAAgAAANwAAAAGAAAAAQAAAOwAAAABIAAAAQAAAAwB" + + "AAADIAAAAQAAACQBAAACIAAADwAAACgBAAAEIAAAAgAAAH8CAAAAIAAAAQAAAI4CAAADEAAAAgAA" + + "AJwCAAAGIAAAAQAAAKwCAAAAEAAAAQAAALwCAAA="); + /* + * base64 encoded class/dex file for + * Base64 generated using: + * % javac Test2036.java + * % d8 Test2035\$SubSubTransform.class + * % base64 classes.dex| sed 's:^:":' | sed 's:$:" +:' + * + * package art; + * public static class SubSubTransform extends SubTransform { + * public SubTransform() {} + * public static int FooBar() { return 0xDEADBEEF; } + * public int foo; + * public static double STATIC_FOO; + * } + */ + private static final byte[] DEX_BYTES_SUB_SUB = + Base64.getDecoder() + .decode( + "ZGV4CjAzNQATpeOoP5d8wSzfcaS99KwXbb1DsEne1tDsAwAAcAAAAHhWNBIAAAAAAAAAADQDAAAS" + + "AAAAcAAAAAgAAAC4AAAAAgAAANgAAAACAAAA8AAAAAMAAAAAAQAAAQAAABgBAAC0AgAAOAEAAHAB" + + "AAB4AQAAewEAAIMBAACGAQAApgEAAMMBAADTAQAA9wEAABcCAAAjAgAANAIAAEMCAABGAgAAUwIA" + + "AFgCAABeAgAAZQIAAAEAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAMAAAAAwAAAAEAAAAAAAAA" + + "DAAAAAcAAAAAAAAAAgAAAAkAAAACAAEADgAAAAIAAQAAAAAAAgAAAAIAAAADAAEAAAAAAAIAAAAB" + + "AAAAAwAAAAAAAAALAAAAJAMAAAIDAAAAAAAAAQAAAAAAAABoAQAABAAAABQA776t3g8AAQABAAEA" + + "AABsAQAABAAAAHAQAgAAAA4AIQAOACAADgAGPGluaXQ+AAFEAAZGb29CYXIAAUkAHkxhcnQvVGVz" + + "dDIwMzYkU3ViU3ViVHJhbnNmb3JtOwAbTGFydC9UZXN0MjAzNiRTdWJUcmFuc2Zvcm07AA5MYXJ0" + + "L1Rlc3QyMDM2OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9h" + + "bm5vdGF0aW9uL0lubmVyQ2xhc3M7AApTVEFUSUNfRk9PAA9TdWJTdWJUcmFuc2Zvcm0ADVRlc3Qy" + + "MDM2LmphdmEAAVYAC2FjY2Vzc0ZsYWdzAANmb28ABG5hbWUABXZhbHVlAIsBfn5EOHsiY29tcGls" + + "YXRpb24tbW9kZSI6ImRlYnVnIiwiaGFzLWNoZWNrc3VtcyI6ZmFsc2UsIm1pbi1hcGkiOjEsInNo" + + "YS0xIjoiMmQwMTQyMTI2NWUyNTExYzkwZTFlNjY1NTQ1YTMzOWIzYzk5YzBhZiIsInZlcnNpb24i" + + "OiIyLjIuMy1kZXYifQACBQEQGAQCBgINBAkPFwoBAQIAAAkBAQCBgATQAgEJuAIAAAAAAgAAAPMC" + + "AAD5AgAAGAMAAAAAAAAAAAAAAAAAAA8AAAAAAAAAAQAAAAAAAAABAAAAEgAAAHAAAAACAAAACAAA" + + "ALgAAAADAAAAAgAAANgAAAAEAAAAAgAAAPAAAAAFAAAAAwAAAAABAAAGAAAAAQAAABgBAAABIAAA" + + "AgAAADgBAAADIAAAAgAAAGgBAAACIAAAEgAAAHABAAAEIAAAAgAAAPMCAAAAIAAAAQAAAAIDAAAD" + + "EAAAAgAAABQDAAAGIAAAAQAAACQDAAAAEAAAAQAAADQDAAA="); + + public static void run() throws Exception { + Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); + doTest(); + } + + public static void doTest() throws Exception { + SubSubSubTransform t = new SubSubSubTransform(); + System.out.println("value-getter is " + t.getFoo()); + System.out.println("value is " + t.foo); + System.out.println("static-call is " + SubSubSubTransform.FooBar()); + System.out.println("static-value is " + SubSubSubTransform.STATIC_FOO); + Redefinition.doMultiStructuralClassRedefinition( + new Redefinition.DexOnlyClassDefinition(Transform.class, DEX_BYTES), + new Redefinition.DexOnlyClassDefinition(SubSubTransform.class, DEX_BYTES_SUB_SUB)); + System.out.println("value-getter is " + t.getFoo()); + System.out.println("value is " + t.foo); + System.out.println("static-call is " + SubSubSubTransform.FooBar()); + System.out.println("static-value is " + SubSubSubTransform.STATIC_FOO); + } +} diff --git a/test/2036-structural-subclass-shadow/src/Main.java b/test/2036-structural-subclass-shadow/src/Main.java new file mode 100644 index 0000000000..e0477b0c23 --- /dev/null +++ b/test/2036-structural-subclass-shadow/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + System.out.println("FAIL: Test is only for art!"); + } +} diff --git a/test/knownfailures.json b/test/knownfailures.json index 398f1235a2..3c72608208 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -1222,7 +1222,8 @@ "2005-pause-all-redefine-multithreaded", "2006-virtual-structural-finalizing", "2007-virtual-structural-finalizable", - "2035-structural-native-method"], + "2035-structural-native-method", + "2036-structural-subclass-shadow"], "variant": "jvm", "description": ["Doesn't run on RI."] }, |