001/*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.jetbrains.jet.rt.signature;
018
019import jet.typeinfo.TypeInfoVariance;
020
021/**
022 * @see SignatureReader
023 */
024public class JetSignatureReader {
025    
026    private final String signature;
027
028    public JetSignatureReader(String signature) {
029        this.signature = signature;
030    }
031
032
033    public void accept(JetSignatureVisitor v) {
034        String signature = this.signature;
035        int len = signature.length();
036        int pos = acceptFormalTypeParameters(v);
037
038        if (signature.charAt(pos) == '(') {
039            pos++;
040            while (signature.charAt(pos) != ')') {
041                pos = parseType(signature, pos, v.visitParameterType());
042            }
043            pos = parseType(signature, pos + 1, v.visitReturnType());
044            while (pos < len) {
045                pos = parseType(signature, pos + 1, v.visitExceptionType());
046            }
047        }
048        else {
049            pos = parseType(signature, pos, v.visitSuperclass());
050            while (pos < len) {
051                pos = parseType(signature, pos, v.visitInterface());
052            }
053        }
054        
055        if (pos != signature.length()) {
056            throw new IllegalStateException();
057        }
058    }
059
060    public int acceptFormalTypeParameters(JetSignatureVisitor v) {
061        int pos;
062        char c;
063        if (signature.length() > 0 && signature.charAt(0) == '<') {
064            pos = 1;
065            do {
066                TypeInfoVariance variance;
067                boolean reified = true;
068                
069                if (signature.substring(pos).startsWith("erased ")) {
070                    reified = false;
071                    pos += "erased ".length();
072                }
073                if (signature.substring(pos).startsWith("in ")) {
074                    variance = TypeInfoVariance.IN;
075                    pos += "in ".length();
076                }
077                else if (signature.substring(pos).startsWith("out ")) {
078                    variance = TypeInfoVariance.OUT;
079                    pos += "out ".length();
080                }
081                else {
082                    variance = TypeInfoVariance.INVARIANT;
083                    pos += "".length();
084                }
085                int end = signature.indexOf(':', pos);
086                if (end < 0) {
087                    throw new IllegalStateException();
088                }
089                String typeParameterName = signature.substring(pos, end);
090                if (typeParameterName.isEmpty()) {
091                    throw new IllegalStateException("incorrect signature: " + signature);
092                }
093                JetSignatureVisitor parameterVisitor = v.visitFormalTypeParameter(typeParameterName, variance, reified);
094                pos = end + 1;
095
096                c = signature.charAt(pos);
097                if (c == 'L' || c == 'M' || c == '[' || c == 'T' || c == '?') {
098                    pos = parseType(signature, pos, parameterVisitor.visitClassBound());
099                }
100
101                while ((c = signature.charAt(pos)) == ':') {
102                    ++pos;
103                    pos = parseType(signature, pos, parameterVisitor.visitInterfaceBound());
104                }
105                
106                parameterVisitor.visitFormalTypeParameterEnd();
107            } while (c != '>');
108            ++pos;
109        }
110        else {
111            pos = 0;
112        }
113        return pos;
114    }
115    
116    public void acceptFormalTypeParametersOnly(JetSignatureVisitor v) {
117        int r = acceptFormalTypeParameters(v);
118        if (r != signature.length()) {
119            throw new IllegalStateException();
120        }
121    }
122
123    public int acceptType(JetSignatureVisitor v) {
124        return parseType(this.signature, 0, v);
125    }
126
127    public void acceptTypeOnly(JetSignatureVisitor v) {
128        int r = acceptType(v);
129        if (r != signature.length()) {
130            throw new IllegalStateException();
131        }
132    }
133
134
135    private static int parseType(
136            String signature,
137            int pos,
138            JetSignatureVisitor v)
139    {
140        if (signature.length() == 0) {
141            throw new IllegalStateException();
142        }
143        
144        char c;
145        int start;
146        int end;
147        boolean visited;
148        boolean inner;
149
150        boolean nullable;
151        if (signature.charAt(pos) == '?') {
152            nullable = true;
153            pos++;
154        }
155        else {
156            nullable = false;
157        }
158
159        switch (c = signature.charAt(pos++)) {
160            case 'Z':
161            case 'C':
162            case 'B':
163            case 'S':
164            case 'I':
165            case 'F':
166            case 'J':
167            case 'D':
168            case 'V':
169                v.visitBaseType(c, nullable);
170                return pos;
171
172            case '[':
173                switch (c = signature.charAt(pos)) {
174                    case '+':
175                    case '-':
176                        return parseType(signature, pos + 1, v.visitArrayType(nullable, JetSignatureVariance.parseVariance(c)));
177                    default:
178                        return parseType(signature, pos, v.visitArrayType(nullable, JetSignatureVariance.INVARIANT));
179                }
180
181            case 'T':
182                end = signature.indexOf(';', pos);
183                v.visitTypeVariable(signature.substring(pos, end), nullable);
184                return end + 1;
185
186            case 'L':
187            case 'M':
188                boolean forceReal = signature.charAt(pos - 1) == 'M';
189                start = pos;
190                visited = false;
191                inner = false;
192                while (true) {
193                    switch (c = signature.charAt(pos++)) {
194                        case '.':
195                        case ';':
196                            if (!visited) {
197                                parseTypeConstructor(signature, v, start, pos, inner, nullable, forceReal);
198                            }
199                            if (c == ';') {
200                                v.visitEnd();
201                                return pos;
202                            }
203                            visited = false;
204                            inner = true;
205                            break;
206
207                        case '<':
208                            parseTypeConstructor(signature, v, start, pos, inner, nullable, forceReal);
209                            visited = true;
210                            pos = parseTypeArguments(signature, pos, v);
211
212                        default:
213                            break;
214                    }
215                }
216            default:
217                throw new IllegalStateException();
218        }
219    }
220
221    private static void parseTypeConstructor(
222            String signature,
223            JetSignatureVisitor v,
224            int start,
225            int pos,
226            boolean inner,
227            boolean nullable,
228            boolean forceReal
229    ) {
230        String name = signature.substring(start, pos - 1);
231        if (inner) {
232            v.visitInnerClassType(name, nullable, forceReal);
233        }
234        else {
235            v.visitClassType(name, nullable, forceReal);
236        }
237    }
238
239    private static int parseTypeArguments(String signature, int pos, JetSignatureVisitor v) {
240        char c;
241        while (true) {
242            switch (c = signature.charAt(pos)) {
243                case '>':
244                    return pos;
245                case '*':
246                    ++pos;
247                    v.visitTypeArgument();
248                    break;
249                case '+':
250                case '-':
251                    pos = parseType(signature,
252                                    pos + 1,
253                                    v.visitTypeArgument(JetSignatureVariance.parseVariance(c)));
254                    break;
255                default:
256                    pos = parseType(signature,
257                                    pos,
258                                    v.visitTypeArgument(JetSignatureVariance.INVARIANT));
259                    break;
260            }
261        }
262    }
263}