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    
017    package org.jetbrains.jet.rt.signature;
018    
019    import jet.typeinfo.TypeInfoVariance;
020    
021    /**
022     * @see SignatureReader
023     */
024    public 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    }