Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/langtools/src/share/classes/com/sun/tools/classfile/Dependencies.java
38899 views
1
/*
2
* Copyright (c) 2009, 2013, 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
package com.sun.tools.classfile;
26
27
import java.util.Deque;
28
import java.util.HashMap;
29
import java.util.HashSet;
30
import java.util.LinkedList;
31
import java.util.List;
32
import java.util.Map;
33
import java.util.Set;
34
import java.util.regex.Pattern;
35
36
import com.sun.tools.classfile.Dependency.Filter;
37
import com.sun.tools.classfile.Dependency.Finder;
38
import com.sun.tools.classfile.Dependency.Location;
39
import com.sun.tools.classfile.Type.ArrayType;
40
import com.sun.tools.classfile.Type.ClassSigType;
41
import com.sun.tools.classfile.Type.ClassType;
42
import com.sun.tools.classfile.Type.MethodType;
43
import com.sun.tools.classfile.Type.SimpleType;
44
import com.sun.tools.classfile.Type.TypeParamType;
45
import com.sun.tools.classfile.Type.WildcardType;
46
import static com.sun.tools.classfile.ConstantPool.*;
47
48
/**
49
* A framework for determining {@link Dependency dependencies} between class files.
50
*
51
* A {@link Dependency.Finder finder} is used to identify the dependencies of
52
* individual classes. Some finders may return subtypes of {@code Dependency} to
53
* further characterize the type of dependency, such as a dependency on a
54
* method within a class.
55
*
56
* A {@link Dependency.Filter filter} may be used to restrict the set of
57
* dependencies found by a finder.
58
*
59
* Dependencies that are found may be passed to a {@link Dependencies.Recorder
60
* recorder} so that the dependencies can be stored in a custom data structure.
61
*/
62
public class Dependencies {
63
/**
64
* Thrown when a class file cannot be found.
65
*/
66
public static class ClassFileNotFoundException extends Exception {
67
private static final long serialVersionUID = 3632265927794475048L;
68
69
public ClassFileNotFoundException(String className) {
70
super(className);
71
this.className = className;
72
}
73
74
public ClassFileNotFoundException(String className, Throwable cause) {
75
this(className);
76
initCause(cause);
77
}
78
79
public final String className;
80
}
81
82
/**
83
* Thrown when an exception is found processing a class file.
84
*/
85
public static class ClassFileError extends Error {
86
private static final long serialVersionUID = 4111110813961313203L;
87
88
public ClassFileError(Throwable cause) {
89
initCause(cause);
90
}
91
}
92
93
/**
94
* Service provider interface to locate and read class files.
95
*/
96
public interface ClassFileReader {
97
/**
98
* Get the ClassFile object for a specified class.
99
* @param className the name of the class to be returned.
100
* @return the ClassFile for the given class
101
* @throws Dependencies.ClassFileNotFoundException if the classfile cannot be
102
* found
103
*/
104
public ClassFile getClassFile(String className)
105
throws ClassFileNotFoundException;
106
}
107
108
/**
109
* Service provide interface to handle results.
110
*/
111
public interface Recorder {
112
/**
113
* Record a dependency that has been found.
114
* @param d
115
*/
116
public void addDependency(Dependency d);
117
}
118
119
/**
120
* Get the default finder used to locate the dependencies for a class.
121
* @return the default finder
122
*/
123
public static Finder getDefaultFinder() {
124
return new APIDependencyFinder(AccessFlags.ACC_PRIVATE);
125
}
126
127
/**
128
* Get a finder used to locate the API dependencies for a class.
129
* These include the superclass, superinterfaces, and classes referenced in
130
* the declarations of fields and methods. The fields and methods that
131
* are checked can be limited according to a specified access.
132
* The access parameter must be one of {@link AccessFlags#ACC_PUBLIC ACC_PUBLIC},
133
* {@link AccessFlags#ACC_PRIVATE ACC_PRIVATE},
134
* {@link AccessFlags#ACC_PROTECTED ACC_PROTECTED}, or 0 for
135
* package private access. Members with greater than or equal accessibility
136
* to that specified will be searched for dependencies.
137
* @param access the access of members to be checked
138
* @return an API finder
139
*/
140
public static Finder getAPIFinder(int access) {
141
return new APIDependencyFinder(access);
142
}
143
144
/**
145
* Get a finder to do class dependency analysis.
146
*
147
* @return a Class dependency finder
148
*/
149
public static Finder getClassDependencyFinder() {
150
return new ClassDependencyFinder();
151
}
152
153
/**
154
* Get the finder used to locate the dependencies for a class.
155
* @return the finder
156
*/
157
public Finder getFinder() {
158
if (finder == null)
159
finder = getDefaultFinder();
160
return finder;
161
}
162
163
/**
164
* Set the finder used to locate the dependencies for a class.
165
* @param f the finder
166
*/
167
public void setFinder(Finder f) {
168
f.getClass(); // null check
169
finder = f;
170
}
171
172
/**
173
* Get the default filter used to determine included when searching
174
* the transitive closure of all the dependencies.
175
* Unless overridden, the default filter accepts all dependencies.
176
* @return the default filter.
177
*/
178
public static Filter getDefaultFilter() {
179
return DefaultFilter.instance();
180
}
181
182
/**
183
* Get a filter which uses a regular expression on the target's class name
184
* to determine if a dependency is of interest.
185
* @param pattern the pattern used to match the target's class name
186
* @return a filter for matching the target class name with a regular expression
187
*/
188
public static Filter getRegexFilter(Pattern pattern) {
189
return new TargetRegexFilter(pattern);
190
}
191
192
/**
193
* Get a filter which checks the package of a target's class name
194
* to determine if a dependency is of interest. The filter checks if the
195
* package of the target's class matches any of a set of given package
196
* names. The match may optionally match subpackages of the given names as well.
197
* @param packageNames the package names used to match the target's class name
198
* @param matchSubpackages whether or not to match subpackages as well
199
* @return a filter for checking the target package name against a list of package names
200
*/
201
public static Filter getPackageFilter(Set<String> packageNames, boolean matchSubpackages) {
202
return new TargetPackageFilter(packageNames, matchSubpackages);
203
}
204
205
/**
206
* Get the filter used to determine the dependencies included when searching
207
* the transitive closure of all the dependencies.
208
* Unless overridden, the default filter accepts all dependencies.
209
* @return the filter
210
*/
211
public Filter getFilter() {
212
if (filter == null)
213
filter = getDefaultFilter();
214
return filter;
215
}
216
217
/**
218
* Set the filter used to determine the dependencies included when searching
219
* the transitive closure of all the dependencies.
220
* @param f the filter
221
*/
222
public void setFilter(Filter f) {
223
f.getClass(); // null check
224
filter = f;
225
}
226
227
/**
228
* Find the dependencies of a class, using the current
229
* {@link Dependencies#getFinder finder} and
230
* {@link Dependencies#getFilter filter}.
231
* The search may optionally include the transitive closure of all the
232
* filtered dependencies, by also searching in the classes named in those
233
* dependencies.
234
* @param classFinder a finder to locate class files
235
* @param rootClassNames the names of the root classes from which to begin
236
* searching
237
* @param transitiveClosure whether or not to also search those classes
238
* named in any filtered dependencies that are found.
239
* @return the set of dependencies that were found
240
* @throws ClassFileNotFoundException if a required class file cannot be found
241
* @throws ClassFileError if an error occurs while processing a class file,
242
* such as an error in the internal class file structure.
243
*/
244
public Set<Dependency> findAllDependencies(
245
ClassFileReader classFinder, Set<String> rootClassNames,
246
boolean transitiveClosure)
247
throws ClassFileNotFoundException {
248
final Set<Dependency> results = new HashSet<Dependency>();
249
Recorder r = new Recorder() {
250
public void addDependency(Dependency d) {
251
results.add(d);
252
}
253
};
254
findAllDependencies(classFinder, rootClassNames, transitiveClosure, r);
255
return results;
256
}
257
258
/**
259
* Find the dependencies of a class, using the current
260
* {@link Dependencies#getFinder finder} and
261
* {@link Dependencies#getFilter filter}.
262
* The search may optionally include the transitive closure of all the
263
* filtered dependencies, by also searching in the classes named in those
264
* dependencies.
265
* @param classFinder a finder to locate class files
266
* @param rootClassNames the names of the root classes from which to begin
267
* searching
268
* @param transitiveClosure whether or not to also search those classes
269
* named in any filtered dependencies that are found.
270
* @param recorder a recorder for handling the results
271
* @throws ClassFileNotFoundException if a required class file cannot be found
272
* @throws ClassFileError if an error occurs while processing a class file,
273
* such as an error in the internal class file structure.
274
*/
275
public void findAllDependencies(
276
ClassFileReader classFinder, Set<String> rootClassNames,
277
boolean transitiveClosure, Recorder recorder)
278
throws ClassFileNotFoundException {
279
Set<String> doneClasses = new HashSet<String>();
280
281
getFinder(); // ensure initialized
282
getFilter(); // ensure initialized
283
284
// Work queue of names of classfiles to be searched.
285
// Entries will be unique, and for classes that do not yet have
286
// dependencies in the results map.
287
Deque<String> deque = new LinkedList<String>(rootClassNames);
288
289
String className;
290
while ((className = deque.poll()) != null) {
291
assert (!doneClasses.contains(className));
292
doneClasses.add(className);
293
294
ClassFile cf = classFinder.getClassFile(className);
295
296
// The following code just applies the filter to the dependencies
297
// followed for the transitive closure.
298
for (Dependency d: finder.findDependencies(cf)) {
299
recorder.addDependency(d);
300
if (transitiveClosure && filter.accepts(d)) {
301
String cn = d.getTarget().getClassName();
302
if (!doneClasses.contains(cn))
303
deque.add(cn);
304
}
305
}
306
}
307
}
308
309
private Filter filter;
310
private Finder finder;
311
312
/**
313
* A location identifying a class.
314
*/
315
static class SimpleLocation implements Location {
316
public SimpleLocation(String name) {
317
this.name = name;
318
this.className = name.replace('/', '.');
319
}
320
321
public String getName() {
322
return name;
323
}
324
325
public String getClassName() {
326
return className;
327
}
328
329
public String getPackageName() {
330
int i = name.lastIndexOf('/');
331
return (i > 0) ? name.substring(0, i).replace('/', '.') : "";
332
}
333
334
@Override
335
public boolean equals(Object other) {
336
if (this == other)
337
return true;
338
if (!(other instanceof SimpleLocation))
339
return false;
340
return (name.equals(((SimpleLocation) other).name));
341
}
342
343
@Override
344
public int hashCode() {
345
return name.hashCode();
346
}
347
348
@Override
349
public String toString() {
350
return name;
351
}
352
353
private String name;
354
private String className;
355
}
356
357
/**
358
* A dependency of one class on another.
359
*/
360
static class SimpleDependency implements Dependency {
361
public SimpleDependency(Location origin, Location target) {
362
this.origin = origin;
363
this.target = target;
364
}
365
366
public Location getOrigin() {
367
return origin;
368
}
369
370
public Location getTarget() {
371
return target;
372
}
373
374
@Override
375
public boolean equals(Object other) {
376
if (this == other)
377
return true;
378
if (!(other instanceof SimpleDependency))
379
return false;
380
SimpleDependency o = (SimpleDependency) other;
381
return (origin.equals(o.origin) && target.equals(o.target));
382
}
383
384
@Override
385
public int hashCode() {
386
return origin.hashCode() * 31 + target.hashCode();
387
}
388
389
@Override
390
public String toString() {
391
return origin + ":" + target;
392
}
393
394
private Location origin;
395
private Location target;
396
}
397
398
399
/**
400
* This class accepts all dependencies.
401
*/
402
static class DefaultFilter implements Filter {
403
private static DefaultFilter instance;
404
405
static DefaultFilter instance() {
406
if (instance == null)
407
instance = new DefaultFilter();
408
return instance;
409
}
410
411
public boolean accepts(Dependency dependency) {
412
return true;
413
}
414
}
415
416
/**
417
* This class accepts those dependencies whose target's class name matches a
418
* regular expression.
419
*/
420
static class TargetRegexFilter implements Filter {
421
TargetRegexFilter(Pattern pattern) {
422
this.pattern = pattern;
423
}
424
425
public boolean accepts(Dependency dependency) {
426
return pattern.matcher(dependency.getTarget().getClassName()).matches();
427
}
428
429
private final Pattern pattern;
430
}
431
432
/**
433
* This class accepts those dependencies whose class name is in a given
434
* package.
435
*/
436
static class TargetPackageFilter implements Filter {
437
TargetPackageFilter(Set<String> packageNames, boolean matchSubpackages) {
438
for (String pn: packageNames) {
439
if (pn.length() == 0) // implies null check as well
440
throw new IllegalArgumentException();
441
}
442
this.packageNames = packageNames;
443
this.matchSubpackages = matchSubpackages;
444
}
445
446
public boolean accepts(Dependency dependency) {
447
String pn = dependency.getTarget().getPackageName();
448
if (packageNames.contains(pn))
449
return true;
450
451
if (matchSubpackages) {
452
for (String n: packageNames) {
453
if (pn.startsWith(n + "."))
454
return true;
455
}
456
}
457
458
return false;
459
}
460
461
private final Set<String> packageNames;
462
private final boolean matchSubpackages;
463
}
464
465
/**
466
* This class identifies class names directly or indirectly in the constant pool.
467
*/
468
static class ClassDependencyFinder extends BasicDependencyFinder {
469
public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
470
Visitor v = new Visitor(classfile);
471
for (CPInfo cpInfo: classfile.constant_pool.entries()) {
472
v.scan(cpInfo);
473
}
474
try {
475
v.addClass(classfile.super_class);
476
v.addClasses(classfile.interfaces);
477
v.scan(classfile.attributes);
478
479
for (Field f : classfile.fields) {
480
v.scan(f.descriptor, f.attributes);
481
}
482
for (Method m : classfile.methods) {
483
v.scan(m.descriptor, m.attributes);
484
Exceptions_attribute e =
485
(Exceptions_attribute)m.attributes.get(Attribute.Exceptions);
486
if (e != null) {
487
v.addClasses(e.exception_index_table);
488
}
489
}
490
} catch (ConstantPoolException e) {
491
throw new ClassFileError(e);
492
}
493
494
return v.deps;
495
}
496
}
497
498
/**
499
* This class identifies class names in the signatures of classes, fields,
500
* and methods in a class.
501
*/
502
static class APIDependencyFinder extends BasicDependencyFinder {
503
APIDependencyFinder(int access) {
504
switch (access) {
505
case AccessFlags.ACC_PUBLIC:
506
case AccessFlags.ACC_PROTECTED:
507
case AccessFlags.ACC_PRIVATE:
508
case 0:
509
showAccess = access;
510
break;
511
default:
512
throw new IllegalArgumentException("invalid access 0x"
513
+ Integer.toHexString(access));
514
}
515
}
516
517
public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
518
try {
519
Visitor v = new Visitor(classfile);
520
v.addClass(classfile.super_class);
521
v.addClasses(classfile.interfaces);
522
// inner classes?
523
for (Field f : classfile.fields) {
524
if (checkAccess(f.access_flags))
525
v.scan(f.descriptor, f.attributes);
526
}
527
for (Method m : classfile.methods) {
528
if (checkAccess(m.access_flags)) {
529
v.scan(m.descriptor, m.attributes);
530
Exceptions_attribute e =
531
(Exceptions_attribute) m.attributes.get(Attribute.Exceptions);
532
if (e != null)
533
v.addClasses(e.exception_index_table);
534
}
535
}
536
return v.deps;
537
} catch (ConstantPoolException e) {
538
throw new ClassFileError(e);
539
}
540
}
541
542
boolean checkAccess(AccessFlags flags) {
543
// code copied from javap.Options.checkAccess
544
boolean isPublic = flags.is(AccessFlags.ACC_PUBLIC);
545
boolean isProtected = flags.is(AccessFlags.ACC_PROTECTED);
546
boolean isPrivate = flags.is(AccessFlags.ACC_PRIVATE);
547
boolean isPackage = !(isPublic || isProtected || isPrivate);
548
549
if ((showAccess == AccessFlags.ACC_PUBLIC) && (isProtected || isPrivate || isPackage))
550
return false;
551
else if ((showAccess == AccessFlags.ACC_PROTECTED) && (isPrivate || isPackage))
552
return false;
553
else if ((showAccess == 0) && (isPrivate))
554
return false;
555
else
556
return true;
557
}
558
559
private int showAccess;
560
}
561
562
static abstract class BasicDependencyFinder implements Finder {
563
private Map<String,Location> locations = new HashMap<String,Location>();
564
565
Location getLocation(String className) {
566
Location l = locations.get(className);
567
if (l == null)
568
locations.put(className, l = new SimpleLocation(className));
569
return l;
570
}
571
572
class Visitor implements ConstantPool.Visitor<Void,Void>, Type.Visitor<Void, Void> {
573
private ConstantPool constant_pool;
574
private Location origin;
575
Set<Dependency> deps;
576
577
Visitor(ClassFile classFile) {
578
try {
579
constant_pool = classFile.constant_pool;
580
origin = getLocation(classFile.getName());
581
deps = new HashSet<Dependency>();
582
} catch (ConstantPoolException e) {
583
throw new ClassFileError(e);
584
}
585
}
586
587
void scan(Descriptor d, Attributes attrs) {
588
try {
589
scan(new Signature(d.index).getType(constant_pool));
590
scan(attrs);
591
} catch (ConstantPoolException e) {
592
throw new ClassFileError(e);
593
}
594
}
595
596
void scan(CPInfo cpInfo) {
597
cpInfo.accept(this, null);
598
}
599
600
void scan(Type t) {
601
t.accept(this, null);
602
}
603
604
void scan(Attributes attrs) {
605
try {
606
Signature_attribute sa = (Signature_attribute)attrs.get(Attribute.Signature);
607
if (sa != null)
608
scan(sa.getParsedSignature().getType(constant_pool));
609
610
scan((RuntimeVisibleAnnotations_attribute)
611
attrs.get(Attribute.RuntimeVisibleAnnotations));
612
scan((RuntimeVisibleParameterAnnotations_attribute)
613
attrs.get(Attribute.RuntimeVisibleParameterAnnotations));
614
} catch (ConstantPoolException e) {
615
throw new ClassFileError(e);
616
}
617
}
618
619
private void scan(RuntimeAnnotations_attribute attr) throws ConstantPoolException {
620
if (attr == null) {
621
return;
622
}
623
for (int i = 0; i < attr.annotations.length; i++) {
624
int index = attr.annotations[i].type_index;
625
scan(new Signature(index).getType(constant_pool));
626
}
627
}
628
629
private void scan(RuntimeParameterAnnotations_attribute attr) throws ConstantPoolException {
630
if (attr == null) {
631
return;
632
}
633
for (int param = 0; param < attr.parameter_annotations.length; param++) {
634
for (int i = 0; i < attr.parameter_annotations[param].length; i++) {
635
int index = attr.parameter_annotations[param][i].type_index;
636
scan(new Signature(index).getType(constant_pool));
637
}
638
}
639
}
640
641
void addClass(int index) throws ConstantPoolException {
642
if (index != 0) {
643
String name = constant_pool.getClassInfo(index).getBaseName();
644
if (name != null)
645
addDependency(name);
646
}
647
}
648
649
void addClasses(int[] indices) throws ConstantPoolException {
650
for (int i: indices)
651
addClass(i);
652
}
653
654
private void addDependency(String name) {
655
deps.add(new SimpleDependency(origin, getLocation(name)));
656
}
657
658
// ConstantPool.Visitor methods
659
660
public Void visitClass(CONSTANT_Class_info info, Void p) {
661
try {
662
if (info.getName().startsWith("["))
663
new Signature(info.name_index).getType(constant_pool).accept(this, null);
664
else
665
addDependency(info.getBaseName());
666
return null;
667
} catch (ConstantPoolException e) {
668
throw new ClassFileError(e);
669
}
670
}
671
672
public Void visitDouble(CONSTANT_Double_info info, Void p) {
673
return null;
674
}
675
676
public Void visitFieldref(CONSTANT_Fieldref_info info, Void p) {
677
return visitRef(info, p);
678
}
679
680
public Void visitFloat(CONSTANT_Float_info info, Void p) {
681
return null;
682
}
683
684
public Void visitInteger(CONSTANT_Integer_info info, Void p) {
685
return null;
686
}
687
688
public Void visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) {
689
return visitRef(info, p);
690
}
691
692
public Void visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, Void p) {
693
return null;
694
}
695
696
public Void visitLong(CONSTANT_Long_info info, Void p) {
697
return null;
698
}
699
700
public Void visitMethodHandle(CONSTANT_MethodHandle_info info, Void p) {
701
return null;
702
}
703
704
public Void visitMethodType(CONSTANT_MethodType_info info, Void p) {
705
return null;
706
}
707
708
public Void visitMethodref(CONSTANT_Methodref_info info, Void p) {
709
return visitRef(info, p);
710
}
711
712
public Void visitNameAndType(CONSTANT_NameAndType_info info, Void p) {
713
try {
714
new Signature(info.type_index).getType(constant_pool).accept(this, null);
715
return null;
716
} catch (ConstantPoolException e) {
717
throw new ClassFileError(e);
718
}
719
}
720
721
public Void visitString(CONSTANT_String_info info, Void p) {
722
return null;
723
}
724
725
public Void visitUtf8(CONSTANT_Utf8_info info, Void p) {
726
return null;
727
}
728
729
private Void visitRef(CPRefInfo info, Void p) {
730
try {
731
visitClass(info.getClassInfo(), p);
732
return null;
733
} catch (ConstantPoolException e) {
734
throw new ClassFileError(e);
735
}
736
}
737
738
// Type.Visitor methods
739
740
private void findDependencies(Type t) {
741
if (t != null)
742
t.accept(this, null);
743
}
744
745
private void findDependencies(List<? extends Type> ts) {
746
if (ts != null) {
747
for (Type t: ts)
748
t.accept(this, null);
749
}
750
}
751
752
public Void visitSimpleType(SimpleType type, Void p) {
753
return null;
754
}
755
756
public Void visitArrayType(ArrayType type, Void p) {
757
findDependencies(type.elemType);
758
return null;
759
}
760
761
public Void visitMethodType(MethodType type, Void p) {
762
findDependencies(type.paramTypes);
763
findDependencies(type.returnType);
764
findDependencies(type.throwsTypes);
765
findDependencies(type.typeParamTypes);
766
return null;
767
}
768
769
public Void visitClassSigType(ClassSigType type, Void p) {
770
findDependencies(type.superclassType);
771
findDependencies(type.superinterfaceTypes);
772
return null;
773
}
774
775
public Void visitClassType(ClassType type, Void p) {
776
findDependencies(type.outerType);
777
addDependency(type.getBinaryName());
778
findDependencies(type.typeArgs);
779
return null;
780
}
781
782
public Void visitTypeParamType(TypeParamType type, Void p) {
783
findDependencies(type.classBound);
784
findDependencies(type.interfaceBounds);
785
return null;
786
}
787
788
public Void visitWildcardType(WildcardType type, Void p) {
789
findDependencies(type.boundType);
790
return null;
791
}
792
}
793
}
794
}
795
796