1 // Copyright 2017 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package com.google.devtools.build.android.desugar.testdata.java8; 15 16 import com.google.common.collect.ImmutableList; 17 import java.util.ArrayList; 18 import java.util.List; 19 20 /** 21 * Interfaces with default methods are intialized differently from those without default methods. 22 * When we load such an interface, its static intializer will be executed. 23 * 24 * <p>However, interfaces without default methods are only initialized when their non-primitive 25 * fields are accessed. 26 * 27 * <p>Test data for b/38255926 28 */ 29 public class DefaultInterfaceMethodWithStaticInitializer { 30 31 final List<String> initializationOrder = new ArrayList<>(); 32 register(Class<?> enclosingInterfaceClass)33 DefaultInterfaceMethodWithStaticInitializer register(Class<?> enclosingInterfaceClass) { 34 initializationOrder.add(enclosingInterfaceClass.getSimpleName()); 35 return this; 36 } 37 getTime()38 private static long getTime() { 39 return 0; 40 } 41 42 /** The simplest case: direct implementation. */ 43 public static class TestInterfaceSetOne { 44 45 /** 46 * A writable field so that other interfaces can set it in their static initializers. 47 * (b/64290760) 48 */ 49 static long writableStaticField; 50 51 static final DefaultInterfaceMethodWithStaticInitializer RECORDER = 52 new DefaultInterfaceMethodWithStaticInitializer(); 53 54 /** With a default method, this interface should run clinit. */ 55 interface I1 { 56 long NOW = TestInterfaceSetOne.writableStaticField = getTime(); 57 DefaultInterfaceMethodWithStaticInitializer C = RECORDER.register(I1.class); 58 defaultM1()59 default int defaultM1() { 60 return 1; 61 } 62 } 63 64 /** With a default method, this interface should run clinit. */ 65 interface I2 { 66 long NOW = TestInterfaceSetOne.writableStaticField = getTime(); 67 DefaultInterfaceMethodWithStaticInitializer D = RECORDER.register(I2.class); 68 defaultM2()69 default int defaultM2() { 70 return 10; 71 } 72 } 73 74 /** Class to trigger the clinit. */ 75 public static class C implements I1, I2 { sum()76 public int sum() { 77 return defaultM1() + defaultM2(); 78 } 79 } 80 getExpectedInitializationOrder()81 public static ImmutableList<String> getExpectedInitializationOrder() { 82 return ImmutableList.of(I1.class.getSimpleName(), I2.class.getSimpleName()); 83 } 84 getRealInitializationOrder()85 public static ImmutableList<String> getRealInitializationOrder() { 86 return ImmutableList.copyOf(RECORDER.initializationOrder); 87 } 88 } 89 90 /** Test for initializer execution order. */ 91 public static class TestInterfaceSetTwo { 92 93 static final DefaultInterfaceMethodWithStaticInitializer RECORDER = 94 new DefaultInterfaceMethodWithStaticInitializer(); 95 96 interface I1 { 97 DefaultInterfaceMethodWithStaticInitializer C = RECORDER.register(I1.class); 98 defaultM1()99 default int defaultM1() { 100 return 1; 101 } 102 } 103 104 interface I2 extends I1 { 105 DefaultInterfaceMethodWithStaticInitializer D = RECORDER.register(I2.class); 106 defaultM2()107 default int defaultM2() { 108 return 2; 109 } 110 } 111 112 /** 113 * Loading this class will trigger the execution of the static initializers of I2 and I1. 114 * However, I1 will be loaded first, as I2 extends I1. 115 */ 116 public static class C implements I2, I1 { 117 protected static final Integer INT_VALUE = Integer.valueOf(1); // To create a <clinit> 118 sum()119 public int sum() { 120 return defaultM1() + defaultM2(); 121 } 122 } 123 getExpectedInitializationOrder()124 public static ImmutableList<String> getExpectedInitializationOrder() { 125 return ImmutableList.of(I1.class.getSimpleName(), I2.class.getSimpleName()); 126 } 127 getRealInitializationOrder()128 public static ImmutableList<String> getRealInitializationOrder() { 129 return ImmutableList.copyOf(RECORDER.initializationOrder); 130 } 131 } 132 133 /** Test: I2's <clinit> should not be executed. */ 134 public static class TestInterfaceSetThree { 135 static final DefaultInterfaceMethodWithStaticInitializer RECORDER = 136 new DefaultInterfaceMethodWithStaticInitializer(); 137 138 interface I1 { 139 DefaultInterfaceMethodWithStaticInitializer C = RECORDER.register(I1.class); 140 defaultM1()141 default int defaultM1() { 142 return 6; 143 } 144 } 145 146 interface I2 extends I1 { defaultM2()147 default int defaultM2() { 148 return 5; 149 } 150 } 151 152 /** 153 * Loading this class will trigger the execution of the static initializers of I1. I2's will not 154 * execute. 155 */ 156 public static class C implements I2, I1 { 157 protected static final Integer INT_VALUE = Integer.valueOf(1); // To create a <clinit> 158 sum()159 public int sum() { 160 return defaultM1() + defaultM2(); 161 } 162 } 163 getExpectedInitializationOrder()164 public static ImmutableList<String> getExpectedInitializationOrder() { 165 return ImmutableList.of(I1.class.getSimpleName()); 166 } 167 getRealInitializationOrder()168 public static ImmutableList<String> getRealInitializationOrder() { 169 return ImmutableList.copyOf(RECORDER.initializationOrder); 170 } 171 } 172 } 173