1 /*
  2  * Copyright (c) 2024, 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.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package java.lang.reflect.code.parser.impl;
 27 
 28 import java.lang.reflect.code.parser.impl.Tokens.Token;
 29 import java.lang.reflect.code.parser.impl.Tokens.TokenKind;
 30 import java.lang.reflect.code.type.*;
 31 import java.lang.reflect.code.TypeElement;
 32 import java.lang.reflect.code.type.RecordTypeRef;
 33 import java.lang.reflect.code.type.impl.FieldRefImpl;
 34 import java.lang.reflect.code.type.impl.MethodRefImpl;
 35 import java.lang.reflect.code.type.impl.RecordTypeRefImpl;
 36 import java.util.ArrayList;
 37 import java.util.List;
 38 
 39 public final class DescParser {
 40     private DescParser() {}
 41 
 42     /**
 43      * Parse an externalized type element from its serialized textual form.
 44      * @param desc the serialized externalized type element
 45      * @return the externalized type element
 46      */
 47     public static TypeElement.ExternalizedTypeElement parseExTypeElem(String desc) {
 48         Scanner s = Scanner.factory().newScanner(desc);
 49         s.nextToken();
 50         return parseExTypeElem(s);
 51     }
 52 
 53     /**
 54      * Parse a method reference from its serialized textual form.
 55      *
 56      * @param desc the serialized method reference
 57      * @return the method reference
 58      */
 59     public static MethodRef parseMethodRef(String desc) {
 60         Scanner s = Scanner.factory().newScanner(desc);
 61         s.nextToken();
 62         return parseMethodRef(s);
 63     }
 64 
 65     /**
 66      * Parse a field reference from its serialized textual form.
 67      *
 68      * @param desc the serialized field reference
 69      * @return the field reference
 70      */
 71     public static FieldRef parseFieldRef(String desc) {
 72         Scanner s = Scanner.factory().newScanner(desc);
 73         s.nextToken();
 74         return parseFieldRef(s);
 75     }
 76 
 77     /**
 78      * Parse a record type reference from its serialized textual form.
 79      *
 80      * @param desc the serialized record type reference
 81      * @return the record type reference
 82      */
 83     public static RecordTypeRef parseRecordTypeRef(String desc) {
 84         Scanner s = Scanner.factory().newScanner(desc);
 85         s.nextToken();
 86         return parseRecordTypeRef(s);
 87     }
 88 
 89     public static TypeElement.ExternalizedTypeElement parseExTypeElem(Lexer l) {
 90         StringBuilder identifier = new StringBuilder();
 91         if (l.token().kind == TokenKind.HASH) {
 92             // Quoted identifier
 93             Token t = l.token();
 94             while (t.kind != TokenKind.LT) {
 95                 identifier.append(t.kind == TokenKind.IDENTIFIER ? t.name() : t.kind.name);
 96                 l.nextToken();
 97                 t = l.token();
 98             }
 99         } else {
100             // Qualified identifier
101             Tokens.Token t = l.accept(TokenKind.IDENTIFIER,
102                     TokenKind.PLUS, TokenKind.SUB);
103             identifier.append(t.kind == TokenKind.IDENTIFIER ? t.name() : t.kind.name);
104             while (l.acceptIf(Tokens.TokenKind.DOT)) {
105                 identifier.append(Tokens.TokenKind.DOT.name);
106                 t = l.accept(Tokens.TokenKind.IDENTIFIER);
107                 identifier.append(t.name());
108             }
109         }
110 
111         // Type parameters
112         List<TypeElement.ExternalizedTypeElement> args;
113         if (l.token().kind == Tokens.TokenKind.LT) {
114             args = new ArrayList<>();
115             do {
116                 l.nextToken();
117                 TypeElement.ExternalizedTypeElement arg = parseExTypeElem(l);
118                 args.add(arg);
119             } while (l.token().kind == Tokens.TokenKind.COMMA);
120             l.accept(Tokens.TokenKind.GT);
121         } else {
122             args = List.of();
123         }
124 
125         // @@@ Enclosed/inner classes, separated by $ which may also be parameterized
126 
127         // Parse array-like syntax []+
128         int dims = 0;
129         while (l.acceptIf(Tokens.TokenKind.LBRACKET)) {
130             l.accept(Tokens.TokenKind.RBRACKET);
131             dims++;
132         }
133 
134         TypeElement.ExternalizedTypeElement td = new TypeElement.ExternalizedTypeElement(identifier.toString(), args);
135         if (dims > 0) {
136             // If array-like then type definition becomes a child with identifier [+
137             return new TypeElement.ExternalizedTypeElement("[".repeat(dims), List.of(td));
138         } else {
139             return td;
140         }
141     }
142 
143     static TypeElement parseTypeElement(Lexer l) {
144         TypeElement.ExternalizedTypeElement typeDesc = parseExTypeElem(l);
145         return CoreTypeFactory.CORE_TYPE_FACTORY.constructType(typeDesc);
146     }
147 
148     // (T, T, T, T)R
149     static FunctionType parseMethodType(Lexer l) {
150         List<TypeElement> ptypes = new ArrayList<>();
151         l.accept(Tokens.TokenKind.LPAREN);
152         if (l.token().kind != Tokens.TokenKind.RPAREN) {
153             ptypes.add(parseTypeElement(l));
154             while (l.acceptIf(Tokens.TokenKind.COMMA)) {
155                 ptypes.add(parseTypeElement(l));
156             }
157         }
158         l.accept(Tokens.TokenKind.RPAREN);
159         TypeElement rtype = parseTypeElement(l);
160         return FunctionType.functionType(rtype, ptypes);
161     }
162 
163     static MethodRef parseMethodRef(Lexer l) {
164         TypeElement refType = parseTypeElement(l);
165 
166         l.accept(Tokens.TokenKind.COLCOL);
167 
168         String methodName;
169         if (l.acceptIf(Tokens.TokenKind.LT)) {
170             // Special name such as "<new>"
171             Tokens.Token t = l.accept(Tokens.TokenKind.IDENTIFIER);
172             l.accept(Tokens.TokenKind.GT);
173             methodName = "<" + t.name() + ">";
174         } else {
175             methodName = l.accept(Tokens.TokenKind.IDENTIFIER).name();
176         }
177 
178         FunctionType mtype = parseMethodType(l);
179 
180         return new MethodRefImpl(refType, methodName, mtype);
181     }
182 
183     static FieldRef parseFieldRef(Lexer l) {
184         TypeElement refType = parseTypeElement(l);
185 
186         l.accept(Tokens.TokenKind.COLCOL);
187 
188         String fieldName = l.accept(Tokens.TokenKind.IDENTIFIER).name();
189 
190         FunctionType mtype = parseMethodType(l);
191         if (!mtype.parameterTypes().isEmpty()) {
192             throw new IllegalArgumentException();
193         }
194         return new FieldRefImpl(refType, fieldName, mtype.returnType());
195     }
196 
197     static RecordTypeRef parseRecordTypeRef(Lexer l) {
198         List<RecordTypeRef.ComponentRef> components = new ArrayList<>();
199         l.accept(Tokens.TokenKind.LPAREN);
200         if (l.token().kind != Tokens.TokenKind.RPAREN) {
201             do {
202                 TypeElement componentType = parseTypeElement(l);
203                 String componentName = l.accept(Tokens.TokenKind.IDENTIFIER).name();
204 
205                 components.add(new RecordTypeRef.ComponentRef(componentType, componentName));
206             } while(l.acceptIf(Tokens.TokenKind.COMMA));
207         }
208         l.accept(Tokens.TokenKind.RPAREN);
209         TypeElement recordType = parseTypeElement(l);
210         return new RecordTypeRefImpl(recordType, components);
211     }
212 }