/* * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.graalvm.compiler.truffle; import java.lang.annotation.Annotation; import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; import org.graalvm.util.EconomicMap; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.nodes.Node.Child; import com.oracle.truffle.api.nodes.Node.Children; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; public class TruffleConstantFieldProvider implements ConstantFieldProvider { private final ConstantFieldProvider graalConstantFieldProvider; private final MetaAccessProvider metaAccess; private final EconomicMap<ResolvedJavaField, Annotation[]> cachedAnnotations; public TruffleConstantFieldProvider(ConstantFieldProvider graalConstantFieldProvider, MetaAccessProvider metaAccess) { this.graalConstantFieldProvider = graalConstantFieldProvider; this.metaAccess = metaAccess; this.cachedAnnotations = EconomicMap.create(); } @Override public <T> T readConstantField(ResolvedJavaField field, ConstantFieldTool<T> tool) { boolean isStaticField = field.isStatic(); if (!isStaticField && tool.getReceiver().isNull()) { // can't be optimized return null; } boolean isArrayField = field.getType().isArray(); if (!isArrayField) { // The fast way does not require any annotation processing but only covers the most // frequent cases. It must not be used for array fields as it might return an incorrect // value for the number of stable dimensions. T ret = readConstantFieldFast(field, tool); if (ret != null) { return ret; } } boolean hasObjectKind = field.getType().getJavaKind() == JavaKind.Object; Annotation[] annotations = getAnnotations(field); if (annotations.length > 0) { if (!isStaticField && hasObjectKind && getAnnotation(annotations, Child.class) != null) { return tool.foldConstant(verifyFieldValue(field, tool.readValue(), annotations)); } CompilationFinal compilationFinal = getAnnotation(annotations, CompilationFinal.class); if (compilationFinal != null) { if (isArrayField) { int stableDimensions = actualStableDimensions(field, compilationFinal.dimensions()); return tool.foldStableArray(tool.readValue(), stableDimensions, true); } else { return tool.foldConstant(tool.readValue()); } } if (!isStaticField && hasObjectKind && getAnnotation(annotations, Children.class) != null) { int stableDimensions = isArrayField ? 1 : 0; return tool.foldStableArray(verifyFieldValue(field, tool.readValue(), annotations), stableDimensions, true); } } if (isArrayField) { return readConstantFieldFast(field, tool); } return null; } private Annotation[] getAnnotations(ResolvedJavaField field) { Annotation[] annotations = cachedAnnotations.get(field); if (annotations == null) { annotations = field.getAnnotations(); cachedAnnotations.put(field, annotations); } return annotations; } private static <T extends Annotation> T getAnnotation(Annotation[] annotations, Class<T> annotationClass) { // hardly any fields have more than one annotation, so a simple loop works best for (int i = 0; i < annotations.length; i++) { if (annotations[i].annotationType() == annotationClass) { return annotationClass.cast(annotations[i]); } } return null; } private <T> T readConstantFieldFast(ResolvedJavaField field, ConstantFieldTool<T> tool) { T ret = graalConstantFieldProvider.readConstantField(field, tool); if (ret == null && field.isFinal()) { ret = tool.foldConstant(tool.readValue()); } return ret; } private static int actualStableDimensions(ResolvedJavaField field, int dimensions) { if (dimensions == 0) { return 0; } int arrayDim = getArrayDimensions(field.getType()); if (dimensions < 0) { if (dimensions != -1) { throw new IllegalArgumentException("Negative @CompilationFinal dimensions"); } return arrayDim; } if (dimensions > arrayDim) { throw new IllegalArgumentException(String.format("@CompilationFinal(dimensions=%d) exceeds declared array dimensions (%d) of field %s", dimensions, arrayDim, field)); } return dimensions; } private static int getArrayDimensions(JavaType type) { int dimensions = 0; for (JavaType componentType = type; componentType.isArray(); componentType = componentType.getComponentType()) { dimensions++; } return dimensions; } private JavaConstant verifyFieldValue(ResolvedJavaField field, JavaConstant constant, Annotation[] annotations) { assert getAnnotation(annotations, Child.class) == null || constant.isNull() || metaAccess.lookupJavaType(com.oracle.truffle.api.nodes.Node.class).isAssignableFrom(metaAccess.lookupJavaType(constant)) : String.format( "@Child field value must be a Node: %s, but was: %s", field, constant); assert getAnnotation(annotations, Children.class) == null || constant.isNull() || metaAccess.lookupJavaType(constant).isArray() : String.format("@Children field value must be an array: %s, but was: %s", field, constant); return constant; } }