1 /*
2  * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 
25 package org.graalvm.compiler.core.common.spi;
26 
27 import java.util.Arrays;
28 
29 import org.graalvm.compiler.debug.GraalError;
30 import org.graalvm.compiler.options.Option;
31 import org.graalvm.compiler.options.OptionKey;
32 
33 import jdk.vm.ci.meta.JavaConstant;
34 import jdk.vm.ci.meta.JavaType;
35 import jdk.vm.ci.meta.MetaAccessProvider;
36 import jdk.vm.ci.meta.ResolvedJavaField;
37 import jdk.vm.ci.meta.ResolvedJavaType;
38 
39 /**
40  * Utility for default constant folding semantics for Java fields.
41  */
42 public abstract class JavaConstantFieldProvider implements ConstantFieldProvider {
43 
44     static class Options {
45         @Option(help = "Determines whether to treat final fields with default values as constant.")//
46         public static final OptionKey<Boolean> TrustFinalDefaultFields = new OptionKey<>(true);
47     }
48 
JavaConstantFieldProvider(MetaAccessProvider metaAccess)49     protected JavaConstantFieldProvider(MetaAccessProvider metaAccess) {
50         try {
51             ResolvedJavaType stringType = metaAccess.lookupJavaType(String.class);
52             ResolvedJavaField[] stringFields = stringType.getInstanceFields(false);
53             ResolvedJavaField valueField = null;
54             ResolvedJavaField hashField = null;
55             for (ResolvedJavaField field : stringFields) {
56                 if (field.getName().equals("value")) {
57                     valueField = field;
58                 } else if (field.getName().equals("hash")) {
59                     hashField = field;
60                 }
61             }
62             if (valueField == null) {
63                 throw new GraalError("missing field value " + Arrays.toString(stringFields));
64             }
65             if (hashField == null) {
66                 throw new GraalError("missing field hash " + Arrays.toString(stringFields));
67             }
68             stringValueField = valueField;
69             stringHashField = hashField;
70         } catch (SecurityException e) {
71             throw new GraalError(e);
72         }
73     }
74 
75     @Override
readConstantField(ResolvedJavaField field, ConstantFieldTool<T> tool)76     public <T> T readConstantField(ResolvedJavaField field, ConstantFieldTool<T> tool) {
77         if (isStableField(field, tool)) {
78             JavaConstant value = tool.readValue();
79             if (value != null && isStableFieldValueConstant(field, value, tool)) {
80                 return foldStableArray(value, field, tool);
81             }
82         }
83         if (isFinalField(field, tool)) {
84             JavaConstant value = tool.readValue();
85             if (value != null && isFinalFieldValueConstant(field, value, tool)) {
86                 return tool.foldConstant(value);
87             }
88         }
89         return null;
90     }
91 
foldStableArray(JavaConstant value, ResolvedJavaField field, ConstantFieldTool<T> tool)92     protected <T> T foldStableArray(JavaConstant value, ResolvedJavaField field, ConstantFieldTool<T> tool) {
93         return tool.foldStableArray(value, getArrayDimension(field.getType()), isDefaultStableField(field, tool));
94     }
95 
getArrayDimension(JavaType type)96     private static int getArrayDimension(JavaType type) {
97         int dimensions = 0;
98         JavaType componentType = type;
99         while ((componentType = componentType.getComponentType()) != null) {
100             dimensions++;
101         }
102         return dimensions;
103     }
104 
isArray(ResolvedJavaField field)105     private static boolean isArray(ResolvedJavaField field) {
106         JavaType fieldType = field.getType();
107         return fieldType instanceof ResolvedJavaType && ((ResolvedJavaType) fieldType).isArray();
108     }
109 
110     @SuppressWarnings("unused")
isStableFieldValueConstant(ResolvedJavaField field, JavaConstant value, ConstantFieldTool<?> tool)111     protected boolean isStableFieldValueConstant(ResolvedJavaField field, JavaConstant value, ConstantFieldTool<?> tool) {
112         return !value.isDefaultForKind();
113     }
114 
115     @SuppressWarnings("unused")
isFinalFieldValueConstant(ResolvedJavaField field, JavaConstant value, ConstantFieldTool<?> tool)116     protected boolean isFinalFieldValueConstant(ResolvedJavaField field, JavaConstant value, ConstantFieldTool<?> tool) {
117         return !value.isDefaultForKind() || Options.TrustFinalDefaultFields.getValue(tool.getOptions());
118     }
119 
120     @SuppressWarnings("unused")
isStableField(ResolvedJavaField field, ConstantFieldTool<?> tool)121     protected boolean isStableField(ResolvedJavaField field, ConstantFieldTool<?> tool) {
122         if (isSyntheticEnumSwitchMap(field)) {
123             return true;
124         }
125         if (isWellKnownImplicitStableField(field)) {
126             return true;
127         }
128         if (field.equals(stringHashField)) {
129             return true;
130         }
131         return false;
132     }
133 
isDefaultStableField(ResolvedJavaField field, ConstantFieldTool<?> tool)134     protected boolean isDefaultStableField(ResolvedJavaField field, ConstantFieldTool<?> tool) {
135         assert isStableField(field, tool);
136         if (isSyntheticEnumSwitchMap(field)) {
137             return true;
138         }
139         return false;
140     }
141 
142     @SuppressWarnings("unused")
isFinalField(ResolvedJavaField field, ConstantFieldTool<?> tool)143     protected boolean isFinalField(ResolvedJavaField field, ConstantFieldTool<?> tool) {
144         return field.isFinal();
145     }
146 
isSyntheticEnumSwitchMap(ResolvedJavaField field)147     protected boolean isSyntheticEnumSwitchMap(ResolvedJavaField field) {
148         if (field.isSynthetic() && field.isStatic() && isArray(field)) {
149             String name = field.getName();
150             if (field.isFinal() && name.equals("$VALUES") || name.equals("ENUM$VALUES")) {
151                 // generated int[] field for EnumClass::values()
152                 return true;
153             } else if (name.startsWith("$SwitchMap$") || name.startsWith("$SWITCH_TABLE$")) {
154                 // javac and ecj generate a static field in an inner class for a switch on an enum
155                 // named $SwitchMap$p$k$g$EnumClass and $SWITCH_TABLE$p$k$g$EnumClass, respectively
156                 return true;
157             }
158         }
159         return false;
160     }
161 
162     private final ResolvedJavaField stringValueField;
163     private final ResolvedJavaField stringHashField;
164 
isWellKnownImplicitStableField(ResolvedJavaField field)165     protected boolean isWellKnownImplicitStableField(ResolvedJavaField field) {
166         return field.equals(stringValueField);
167     }
168 }
169