1/*
2 * Copyright (c) 2011, 2015, 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 */
23package jdk.vm.ci.hotspot;
24
25import java.util.ArrayList;
26import java.util.List;
27
28import jdk.vm.ci.common.JVMCIError;
29import jdk.vm.ci.meta.JavaKind;
30import jdk.vm.ci.meta.JavaType;
31import jdk.vm.ci.meta.ResolvedJavaType;
32import jdk.vm.ci.meta.Signature;
33
34/**
35 * Represents a method signature.
36 */
37public class HotSpotSignature implements Signature {
38
39    private final List<String> parameters = new ArrayList<>();
40    private final String returnType;
41    private final String originalString;
42    private ResolvedJavaType[] parameterTypes;
43    private ResolvedJavaType returnTypeCache;
44    private final HotSpotJVMCIRuntimeProvider runtime;
45
46    public HotSpotSignature(HotSpotJVMCIRuntimeProvider runtime, String signature) {
47        this.runtime = runtime;
48        assert signature.length() > 0;
49        this.originalString = signature;
50
51        if (signature.charAt(0) == '(') {
52            int cur = 1;
53            while (cur < signature.length() && signature.charAt(cur) != ')') {
54                int nextCur = parseSignature(signature, cur);
55                parameters.add(signature.substring(cur, nextCur));
56                cur = nextCur;
57            }
58
59            cur++;
60            int nextCur = parseSignature(signature, cur);
61            returnType = signature.substring(cur, nextCur);
62            assert nextCur == signature.length();
63        } else {
64            returnType = null;
65        }
66    }
67
68    public HotSpotSignature(HotSpotJVMCIRuntimeProvider runtime, ResolvedJavaType returnType, ResolvedJavaType... parameterTypes) {
69        this.runtime = runtime;
70        this.parameterTypes = parameterTypes.clone();
71        this.returnTypeCache = returnType;
72        this.returnType = returnType.getName();
73        StringBuilder sb = new StringBuilder("(");
74        for (JavaType type : parameterTypes) {
75            parameters.add(type.getName());
76            sb.append(type.getName());
77        }
78        sb.append(")").append(returnType.getName());
79        this.originalString = sb.toString();
80        assert new HotSpotSignature(runtime, originalString).equals(this);
81    }
82
83    private static int parseSignature(String signature, int start) {
84        int cur = start;
85        char first;
86        do {
87            first = signature.charAt(cur++);
88        } while (first == '[');
89
90        switch (first) {
91            case 'L':
92                while (signature.charAt(cur) != ';') {
93                    cur++;
94                }
95                cur++;
96                break;
97            case 'V':
98            case 'I':
99            case 'B':
100            case 'C':
101            case 'D':
102            case 'F':
103            case 'J':
104            case 'S':
105            case 'Z':
106                break;
107            default:
108                throw new JVMCIError("Invalid character at index %d in signature: %s", cur, signature);
109        }
110        return cur;
111    }
112
113    @Override
114    public int getParameterCount(boolean withReceiver) {
115        return parameters.size() + (withReceiver ? 1 : 0);
116    }
117
118    @Override
119    public JavaKind getParameterKind(int index) {
120        return JavaKind.fromTypeString(parameters.get(index));
121    }
122
123    private static boolean checkValidCache(ResolvedJavaType type, ResolvedJavaType accessingClass) {
124        assert accessingClass != null;
125        if (type == null) {
126            return false;
127        } else if (type instanceof HotSpotResolvedObjectTypeImpl) {
128            return ((HotSpotResolvedObjectTypeImpl) type).isDefinitelyResolvedWithRespectTo(accessingClass);
129        }
130        return true;
131    }
132
133    private static JavaType getUnresolvedOrPrimitiveType(HotSpotJVMCIRuntimeProvider runtime, String name) {
134        if (name.length() == 1) {
135            JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
136            return runtime.getHostJVMCIBackend().getMetaAccess().lookupJavaType(kind.toJavaClass());
137        }
138        return HotSpotUnresolvedJavaType.create(runtime, name);
139    }
140
141    @Override
142    public JavaType getParameterType(int index, ResolvedJavaType accessingClass) {
143        if (accessingClass == null) {
144            // Caller doesn't care about resolution context so return an unresolved
145            // or primitive type (primitive type resolution is context free)
146            return getUnresolvedOrPrimitiveType(runtime, parameters.get(index));
147        }
148        if (parameterTypes == null) {
149            parameterTypes = new ResolvedJavaType[parameters.size()];
150        }
151
152        ResolvedJavaType type = parameterTypes[index];
153        if (!checkValidCache(type, accessingClass)) {
154            JavaType result = runtime.lookupType(parameters.get(index), (HotSpotResolvedObjectType) accessingClass, false);
155            if (result instanceof ResolvedJavaType) {
156                type = (ResolvedJavaType) result;
157                parameterTypes[index] = type;
158            } else {
159                return result;
160            }
161        }
162        return type;
163    }
164
165    @Override
166    public String toMethodDescriptor() {
167        assert originalString.equals(Signature.super.toMethodDescriptor());
168        return originalString;
169    }
170
171    @Override
172    public JavaKind getReturnKind() {
173        return JavaKind.fromTypeString(returnType);
174    }
175
176    @Override
177    public JavaType getReturnType(ResolvedJavaType accessingClass) {
178        if (accessingClass == null) {
179            // Caller doesn't care about resolution context so return an unresolved
180            // or primitive type (primitive type resolution is context free)
181            return getUnresolvedOrPrimitiveType(runtime, returnType);
182        }
183        if (!checkValidCache(returnTypeCache, accessingClass)) {
184            JavaType result = runtime.lookupType(returnType, (HotSpotResolvedObjectType) accessingClass, false);
185            if (result instanceof ResolvedJavaType) {
186                returnTypeCache = (ResolvedJavaType) result;
187            } else {
188                return result;
189            }
190        }
191        return returnTypeCache;
192    }
193
194    @Override
195    public String toString() {
196        return "HotSpotSignature<" + originalString + ">";
197    }
198
199    @Override
200    public boolean equals(Object obj) {
201        if (obj instanceof HotSpotSignature) {
202            HotSpotSignature other = (HotSpotSignature) obj;
203            if (other.originalString.equals(originalString)) {
204                assert other.parameters.equals(parameters);
205                assert other.returnType.equals(returnType);
206                return true;
207            }
208        }
209        return false;
210    }
211
212    @Override
213    public int hashCode() {
214        return originalString.hashCode();
215    }
216}
217