android系统源码编译报错问题分析处理--持续更新
一、build/make/core/base_rules.mk:232: error: packages/services/Car/service: LOCAL_BUILT_MODULE and LOCAL_INSTALLED_MODULE must not be defined by component makefiles
出现此错误时,需要检查Android.mk中include $(CLEAR_VARS)及include $(BUILD_XXX...)是否一一对应了,如果有类似下边的写法,就需要修正
错误:
......
include $(CLEAR_VARS)
......
include $(BUILD_MULTI_PREBUILT)
......
include $(BUILD_STATIC_JAVA_LIBRARY)
......
include $(BUILD_PACKAGE)
include $(CLEAR_VARS)
正确:
include $(CLEAR_VARS)
......
include $(BUILD_MULTI_PREBUILT)
include $(CLEAR_VARS)
......
include $(BUILD_STATIC_JAVA_LIBRARY)
include $(CLEAR_VARS)
......
include $(BUILD_PACKAGE)
二、错误: 程序包androidx.annotation不存在,需要在 Android.mk 中添加如下,根据自己的依赖进行添加
LOCAL_STATIC_ANDROID_LIBRARIES := \
androidx.recyclerview_recyclerview \
androidx.preference_preference \
androidx.appcompat_appcompat \
androidx.annotation_annotation \
androidx.legacy_legacy-preference-v14 \
androidx.leanback_leanback-preference \
androidx.leanback_leanback \
三、javadoc: 错误 - 在 doclet 类com.google.doclava.Doclava中, 方法start已抛出异常错误java.lang.reflect.InvocationTargetException
java.lang.IllegalArgumentException: Unable to find IBtMusicCmd.java. This is usually because doclava has been asked to generate stubs for a file that isn't present in the list of input source files but exists in the input classpath.
IBtMusicCmd是我的aidl文件,代码都编译通过了,没有出现依赖问题,但是最后报出这个错,查了两天才发现这个问题应该是javadoc的版本太低了,我找到了这个系统文件进行了暴力修改后编译正常,但是是否存在隐患还不确定
解决办法:
直接找到external\doclava\src\com\google\doclava\Stubs.java,然后在53行添加if(true) return;如下!目的就是不执行此文件的操作
public class Stubs { public static void writeStubsAndApi(String stubsDir, String apiFile, String dexApiFile, String keepListFile, String removedApiFile, String removedDexApiFile, String exactApiFile, String privateApiFile, String privateDexApiFile, HashSet<String> stubPackages, HashSet<String> stubImportPackages, boolean stubSourceOnly) { if(true) return;
如果为了安全一点,也可以按照下边的方式,只判断是自己的这个模块就跳过就好了,源代码里三个地方添加以下代码,“android.car.xxxx.bt”需要替换为自己冲突的模块包名
if(cl.containingPackage().name().contains("android.car.xxxx.bt")){ System.out.println("=======android.car.xxxx.bt=============continue"); continue; }

1 /* 2 * Copyright (C) 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.doclava; 18 19 import java.io.BufferedOutputStream; 20 import java.io.BufferedReader; 21 import java.io.ByteArrayOutputStream; 22 import java.io.File; 23 import java.io.FileInputStream; 24 import java.io.FileNotFoundException; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.InputStreamReader; 29 import java.io.PrintStream; 30 import java.nio.charset.StandardCharsets; 31 import java.nio.file.Files; 32 import java.nio.file.Paths; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.Collection; 36 import java.util.Collections; 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.Iterator; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Scanner; 43 import java.util.Set; 44 import java.util.function.Predicate; 45 import java.util.regex.Pattern; 46 import java.util.stream.Collectors; 47 48 public class Stubs { 49 public static void writeStubsAndApi(String stubsDir, String apiFile, String dexApiFile, 50 String keepListFile, String removedApiFile, String removedDexApiFile, String exactApiFile, 51 String privateApiFile, String privateDexApiFile, HashSet<String> stubPackages, 52 HashSet<String> stubImportPackages, boolean stubSourceOnly) { 53 //if(true) return; 54 // figure out which classes we need 55 final HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>(); 56 Collection<ClassInfo> all = Converter.allClasses(); 57 Map<PackageInfo, List<ClassInfo>> allClassesByPackage = null; 58 PrintStream apiWriter = null; 59 PrintStream dexApiWriter = null; 60 PrintStream keepListWriter = null; 61 PrintStream removedApiWriter = null; 62 PrintStream removedDexApiWriter = null; 63 PrintStream exactApiWriter = null; 64 PrintStream privateApiWriter = null; 65 PrintStream privateDexApiWriter = null; 66 67 if (apiFile != null) { 68 try { 69 File xml = new File(apiFile); 70 xml.getParentFile().mkdirs(); 71 apiWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(xml))); 72 } catch (FileNotFoundException e) { 73 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(apiFile, 0, 0), 74 "Cannot open file for write."); 75 } 76 } 77 if (dexApiFile != null) { 78 try { 79 File dexApi = new File(dexApiFile); 80 dexApi.getParentFile().mkdirs(); 81 dexApiWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(dexApi))); 82 } catch (FileNotFoundException e) { 83 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(dexApiFile, 0, 0), 84 "Cannot open file for write."); 85 } 86 } 87 if (keepListFile != null) { 88 try { 89 File keepList = new File(keepListFile); 90 keepList.getParentFile().mkdirs(); 91 keepListWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(keepList))); 92 } catch (FileNotFoundException e) { 93 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(keepListFile, 0, 0), 94 "Cannot open file for write."); 95 } 96 } 97 if (removedApiFile != null) { 98 try { 99 File removedApi = new File(removedApiFile); 100 removedApi.getParentFile().mkdirs(); 101 removedApiWriter = new PrintStream( 102 new BufferedOutputStream(new FileOutputStream(removedApi))); 103 } catch (FileNotFoundException e) { 104 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(removedApiFile, 0, 0), 105 "Cannot open file for write"); 106 } 107 } 108 if (removedDexApiFile != null) { 109 try { 110 File removedDexApi = new File(removedDexApiFile); 111 removedDexApi.getParentFile().mkdirs(); 112 removedDexApiWriter = new PrintStream( 113 new BufferedOutputStream(new FileOutputStream(removedDexApi))); 114 } catch (FileNotFoundException e) { 115 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(removedDexApiFile, 0, 0), 116 "Cannot open file for write"); 117 } 118 } 119 if (exactApiFile != null) { 120 try { 121 File exactApi = new File(exactApiFile); 122 exactApi.getParentFile().mkdirs(); 123 exactApiWriter = new PrintStream( 124 new BufferedOutputStream(new FileOutputStream(exactApi))); 125 } catch (FileNotFoundException e) { 126 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(exactApiFile, 0, 0), 127 "Cannot open file for write"); 128 } 129 } 130 if (privateApiFile != null) { 131 try { 132 File privateApi = new File(privateApiFile); 133 privateApi.getParentFile().mkdirs(); 134 privateApiWriter = new PrintStream( 135 new BufferedOutputStream(new FileOutputStream(privateApi))); 136 } catch (FileNotFoundException e) { 137 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(privateApiFile, 0, 0), 138 "Cannot open file for write"); 139 } 140 } 141 if (privateDexApiFile != null) { 142 try { 143 File privateDexApi = new File(privateDexApiFile); 144 privateDexApi.getParentFile().mkdirs(); 145 privateDexApiWriter = new PrintStream( 146 new BufferedOutputStream(new FileOutputStream(privateDexApi))); 147 } catch (FileNotFoundException e) { 148 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(privateDexApiFile, 0, 0), 149 "Cannot open file for write"); 150 } 151 } 152 // If a class is public or protected, not hidden, not imported and marked as included, 153 // then we can't strip it 154 for (ClassInfo cl : all) { 155 if(cl.containingPackage().name().contains("android.car.xxxx.bt")){ 156 System.out.println("=======android.car.xxxx.bt=============continue"); 157 continue; 158 } 159 if (cl.checkLevel() && cl.isIncluded()) { 160 cantStripThis(cl, notStrippable, "0:0", stubImportPackages); 161 } 162 } 163 164 // complain about anything that looks includeable but is not supposed to 165 // be written, e.g. hidden things 166 for (ClassInfo cl : notStrippable) { 167 if(cl.containingPackage().name().contains("android.car.xxxx.bt")){ 168 System.out.println("=======android.car.xxxx.bt=============continue"); 169 continue; 170 } 171 if (!cl.isHiddenOrRemoved()) { 172 for (MethodInfo m : cl.selfMethods()) { 173 if (m.isHiddenOrRemoved()) { 174 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to unavailable method " 175 + m.name()); 176 } else if (m.isDeprecated()) { 177 // don't bother reporting deprecated methods 178 // unless they are public 179 Errors.error(Errors.DEPRECATED, m.position(), "Method " + cl.qualifiedName() + "." 180 + m.name() + " is deprecated"); 181 } 182 183 ClassInfo hiddenClass = findHiddenClasses(m.returnType(), stubImportPackages); 184 if (null != hiddenClass) { 185 if (hiddenClass.qualifiedName() == m.returnType().asClassInfo().qualifiedName()) { 186 // Return type is hidden 187 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Method " + cl.qualifiedName() 188 + "." + m.name() + " returns unavailable type " + hiddenClass.name()); 189 } else { 190 // Return type contains a generic parameter 191 Errors.error(Errors.HIDDEN_TYPE_PARAMETER, m.position(), "Method " + cl.qualifiedName() 192 + "." + m.name() + " returns unavailable type " + hiddenClass.name() 193 + " as a type parameter"); 194 } 195 } 196 197 for (ParameterInfo p : m.parameters()) { 198 TypeInfo t = p.type(); 199 if (!t.isPrimitive()) { 200 hiddenClass = findHiddenClasses(t, stubImportPackages); 201 if (null != hiddenClass) { 202 if (hiddenClass.qualifiedName() == t.asClassInfo().qualifiedName()) { 203 // Parameter type is hidden 204 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), 205 "Parameter of unavailable type " + t.fullName() + " in " + cl.qualifiedName() 206 + "." + m.name() + "()"); 207 } else { 208 // Parameter type contains a generic parameter 209 Errors.error(Errors.HIDDEN_TYPE_PARAMETER, m.position(), 210 "Parameter uses type parameter of unavailable type " + t.fullName() + " in " 211 + cl.qualifiedName() + "." + m.name() + "()"); 212 } 213 } 214 } 215 } 216 } 217 218 // annotations are handled like methods 219 for (MethodInfo m : cl.annotationElements()) { 220 if (m.isHiddenOrRemoved()) { 221 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to unavailable annotation " 222 + m.name()); 223 } 224 225 ClassInfo returnClass = m.returnType().asClassInfo(); 226 if (returnClass != null && returnClass.isHiddenOrRemoved()) { 227 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Annotation '" + m.name() 228 + "' returns unavailable type " + returnClass.name()); 229 } 230 231 for (ParameterInfo p : m.parameters()) { 232 TypeInfo t = p.type(); 233 if (!t.isPrimitive()) { 234 if (t.asClassInfo().isHiddenOrRemoved()) { 235 Errors.error(Errors.UNAVAILABLE_SYMBOL, p.position(), 236 "Reference to unavailable annotation class " + t.fullName()); 237 } 238 } 239 } 240 } 241 } else if (cl.isDeprecated()) { 242 // not hidden, but deprecated 243 Errors.error(Errors.DEPRECATED, cl.position(), "Class " + cl.qualifiedName() 244 + " is deprecated"); 245 } 246 } 247 248 // packages contains all the notStrippable classes mapped by their containing packages 249 HashMap<PackageInfo, List<ClassInfo>> packages = new HashMap<PackageInfo, List<ClassInfo>>(); 250 final HashSet<Pattern> stubPackageWildcards = extractWildcards(stubPackages); 251 for (ClassInfo cl : notStrippable) { 252 if(cl.containingPackage().name().contains("android.car.xxxx.bt")){ 253 System.out.println("=======android.car.xxxx.bt=============continue"); 254 continue; 255 } 256 if (!cl.isDocOnly()) { 257 if (stubSourceOnly && !Files.exists(Paths.get(cl.position().file))) { 258 continue; 259 } 260 if (shouldWriteStub(cl.containingPackage().name(), stubPackages, stubPackageWildcards)) { 261 // write out the stubs 262 if (stubsDir != null) { 263 writeClassFile(stubsDir, notStrippable, cl); 264 } 265 // build class list for api file or keep list file 266 if (apiWriter != null || dexApiWriter != null || keepListWriter != null) { 267 if (packages.containsKey(cl.containingPackage())) { 268 packages.get(cl.containingPackage()).add(cl); 269 } else { 270 ArrayList<ClassInfo> classes = new ArrayList<ClassInfo>(); 271 classes.add(cl); 272 packages.put(cl.containingPackage(), classes); 273 } 274 } 275 } 276 } 277 } 278 279 if (privateApiWriter != null || privateDexApiWriter != null || removedApiWriter != null 280 || removedDexApiWriter != null) { 281 allClassesByPackage = Converter.allClasses().stream() 282 // Make sure that the files only contains information from the required packages. 283 .filter(ci -> stubPackages == null 284 || stubPackages.contains(ci.containingPackage().qualifiedName())) 285 .collect(Collectors.groupingBy(ClassInfo::containingPackage)); 286 } 287 288 final boolean ignoreShown = Doclava.showUnannotated; 289 290 Predicate<MemberInfo> memberIsNotCloned = (x -> !x.isCloned()); 291 292 FilterPredicate apiFilter = new FilterPredicate(new ApiPredicate().setIgnoreShown(ignoreShown)); 293 ApiPredicate apiReference = new ApiPredicate().setIgnoreShown(true); 294 Predicate<MemberInfo> apiEmit = apiFilter.and(new ElidingPredicate(apiReference)); 295 Predicate<MemberInfo> dexApiEmit = memberIsNotCloned.and(apiFilter); 296 297 Predicate<MemberInfo> privateEmit = memberIsNotCloned.and(apiFilter.negate()); 298 Predicate<MemberInfo> privateReference = (x -> true); 299 300 FilterPredicate removedFilter = 301 new FilterPredicate(new ApiPredicate().setIgnoreShown(ignoreShown).setMatchRemoved(true)); 302 ApiPredicate removedReference = new ApiPredicate().setIgnoreShown(true).setIgnoreRemoved(true); 303 Predicate<MemberInfo> removedEmit = removedFilter.and(new ElidingPredicate(removedReference)); 304 Predicate<MemberInfo> removedDexEmit = memberIsNotCloned.and(removedFilter); 305 306 // Write out the current API 307 if (apiWriter != null) { 308 writeApi(apiWriter, packages, apiEmit, apiReference); 309 apiWriter.close(); 310 } 311 312 // Write out the current DEX API 313 if (dexApiWriter != null) { 314 writeDexApi(dexApiWriter, packages, dexApiEmit); 315 dexApiWriter.close(); 316 } 317 318 // Write out the keep list 319 if (keepListWriter != null) { 320 writeKeepList(keepListWriter, packages, notStrippable); 321 keepListWriter.close(); 322 } 323 324 // Write out the private API 325 if (privateApiWriter != null) { 326 writeApi(privateApiWriter, allClassesByPackage, privateEmit, privateReference); 327 privateApiWriter.close(); 328 } 329 330 // Write out the private API 331 if (privateDexApiWriter != null) { 332 writeDexApi(privateDexApiWriter, allClassesByPackage, privateEmit); 333 privateDexApiWriter.close(); 334 } 335 336 // Write out the removed API 337 if (removedApiWriter != null) { 338 writeApi(removedApiWriter, allClassesByPackage, removedEmit, removedReference); 339 removedApiWriter.close(); 340 } 341 342 // Write out the removed DEX API 343 if (removedDexApiWriter != null) { 344 writeDexApi(removedDexApiWriter, allClassesByPackage, removedDexEmit); 345 removedDexApiWriter.close(); 346 } 347 } 348 349 private static boolean shouldWriteStub(final String packageName, 350 final HashSet<String> stubPackages, final HashSet<Pattern> stubPackageWildcards) { 351 if (stubPackages == null) { 352 // There aren't any stub packages set, write all stubs 353 return true; 354 } 355 if (stubPackages.contains(packageName)) { 356 // Stub packages contains package, return true 357 return true; 358 } 359 if (stubPackageWildcards != null) { 360 // Else, we will iterate through the wildcards to see if there's a match 361 for (Pattern wildcard : stubPackageWildcards) { 362 if (wildcard.matcher(packageName).matches()) { 363 return true; 364 } 365 } 366 } 367 return false; 368 } 369 370 private static HashSet<Pattern> extractWildcards(HashSet<String> stubPackages) { 371 HashSet<Pattern> wildcards = null; 372 if (stubPackages != null) { 373 for (Iterator<String> i = stubPackages.iterator(); i.hasNext();) { 374 final String pkg = i.next(); 375 if (pkg.indexOf('*') != -1) { 376 if (wildcards == null) { 377 wildcards = new HashSet<Pattern>(); 378 } 379 // Add the compiled wildcard, replacing * with the regex equivalent 380 wildcards.add(Pattern.compile(pkg.replace("*", ".*?"))); 381 // And remove the raw wildcard from the packages 382 i.remove(); 383 } 384 } 385 } 386 return wildcards; 387 } 388 389 /** 390 * Find references to hidden classes. 391 * 392 * <p>This finds hidden classes that are used by public parts of the API in order to ensure the 393 * API is self consistent and does not reference classes that are not included in 394 * the stubs. Any such references cause an error to be reported. 395 * 396 * <p>A reference to an imported class is not treated as an error, even though imported classes 397 * are hidden from the stub generation. That is because imported classes are, by definition, 398 * excluded from the set of classes for which stubs are required. 399 * 400 * @param ti the type information to examine for references to hidden classes. 401 * @param stubImportPackages the possibly null set of imported package names. 402 * @return a reference to a hidden class or null if there are none 403 */ 404 private static ClassInfo findHiddenClasses(TypeInfo ti, HashSet<String> stubImportPackages) { 405 ClassInfo ci = ti.asClassInfo(); 406 if (ci == null) return null; 407 if (stubImportPackages != null 408 && stubImportPackages.contains(ci.containingPackage().qualifiedName())) { 409 return null; 410 } 411 if (ci.isHiddenOrRemoved()) return ci; 412 if (ti.typeArguments() != null) { 413 for (TypeInfo tii : ti.typeArguments()) { 414 // Avoid infinite recursion in the case of Foo<T extends Foo> 415 if (tii.qualifiedTypeName() != ti.qualifiedTypeName()) { 416 ClassInfo hiddenClass = findHiddenClasses(tii, stubImportPackages); 417 if (hiddenClass != null) return hiddenClass; 418 } 419 } 420 } 421 return null; 422 } 423 424 public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why, 425 HashSet<String> stubImportPackages) { 426 427 if (stubImportPackages != null 428 && stubImportPackages.contains(cl.containingPackage().qualifiedName())) { 429 // if the package is imported then it does not need stubbing. 430 return; 431 } 432 433 if (!notStrippable.add(cl)) { 434 // slight optimization: if it already contains cl, it already contains 435 // all of cl's parents 436 return; 437 } 438 cl.setReasonIncluded(why); 439 440 // cant strip annotations 441 /* 442 * if (cl.annotations() != null){ for (AnnotationInstanceInfo ai : cl.annotations()){ if 443 * (ai.type() != null){ cantStripThis(ai.type(), notStrippable, "1:" + cl.qualifiedName()); } } 444 * } 445 */ 446 // cant strip any public fields or their generics 447 if (cl.selfFields() != null) { 448 for (FieldInfo fInfo : cl.selfFields()) { 449 if (fInfo.type() != null) { 450 if (fInfo.type().asClassInfo() != null) { 451 cantStripThis(fInfo.type().asClassInfo(), notStrippable, "2:" + cl.qualifiedName(), 452 stubImportPackages); 453 } 454 if (fInfo.type().typeArguments() != null) { 455 for (TypeInfo tTypeInfo : fInfo.type().typeArguments()) { 456 if (tTypeInfo.asClassInfo() != null) { 457 cantStripThis(tTypeInfo.asClassInfo(), notStrippable, "3:" + cl.qualifiedName(), 458 stubImportPackages); 459 } 460 } 461 } 462 } 463 } 464 } 465 // cant strip any of the type's generics 466 if (cl.asTypeInfo() != null) { 467 if (cl.asTypeInfo().typeArguments() != null) { 468 for (TypeInfo tInfo : cl.asTypeInfo().typeArguments()) { 469 if (tInfo.asClassInfo() != null) { 470 cantStripThis(tInfo.asClassInfo(), notStrippable, "4:" + cl.qualifiedName(), 471 stubImportPackages); 472 } 473 } 474 } 475 } 476 // cant strip any of the annotation elements 477 // cantStripThis(cl.annotationElements(), notStrippable); 478 // take care of methods 479 cantStripThis(cl.allSelfMethods(), notStrippable, stubImportPackages); 480 cantStripThis(cl.allConstructors(), notStrippable, stubImportPackages); 481 // blow the outer class open if this is an inner class 482 if (cl.containingClass() != null) { 483 cantStripThis(cl.containingClass(), notStrippable, "5:" + cl.qualifiedName(), 484 stubImportPackages); 485 } 486 // blow open super class and interfaces 487 ClassInfo supr = cl.realSuperclass(); 488 if (supr != null) { 489 if (supr.isHiddenOrRemoved()) { 490 // cl is a public class declared as extending a hidden superclass. 491 // this is not a desired practice but it's happened, so we deal 492 // with it by finding the first super class which passes checklevel for purposes of 493 // generating the doc & stub information, and proceeding normally. 494 ClassInfo publicSuper = cl.superclass(); 495 cl.init(cl.asTypeInfo(), cl.realInterfaces(), cl.realInterfaceTypes(), cl.innerClasses(), 496 cl.allConstructors(), cl.allSelfMethods(), cl.annotationElements(), cl.allSelfFields(), 497 cl.enumConstants(), cl.containingPackage(), cl.containingClass(), 498 publicSuper, publicSuper.asTypeInfo(), cl.annotations()); 499 Errors.error(Errors.HIDDEN_SUPERCLASS, cl.position(), "Public class " + cl.qualifiedName() 500 + " stripped of unavailable superclass " + supr.qualifiedName()); 501 } else { 502 cantStripThis(supr, notStrippable, "6:" + cl.realSuperclass().name() + cl.qualifiedName(), 503 stubImportPackages); 504 if (supr.isPrivate()) { 505 Errors.error(Errors.PRIVATE_SUPERCLASS, cl.position(), "Public class " 506 + cl.qualifiedName() + " extends private class " + supr.qualifiedName()); 507 } 508 } 509 } 510 } 511 512 private static void cantStripThis(ArrayList<MethodInfo> mInfos, HashSet<ClassInfo> notStrippable, 513 HashSet<String> stubImportPackages) { 514 // for each method, blow open the parameters, throws and return types. also blow open their 515 // generics 516 if (mInfos != null) { 517 for (MethodInfo mInfo : mInfos) { 518 if (mInfo.getTypeParameters() != null) { 519 for (TypeInfo tInfo : mInfo.getTypeParameters()) { 520 if (tInfo.asClassInfo() != null) { 521 cantStripThis(tInfo.asClassInfo(), notStrippable, "8:" 522 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(), 523 stubImportPackages); 524 } 525 } 526 } 527 if (mInfo.parameters() != null) { 528 for (ParameterInfo pInfo : mInfo.parameters()) { 529 if (pInfo.type() != null && pInfo.type().asClassInfo() != null) { 530 cantStripThis(pInfo.type().asClassInfo(), notStrippable, "9:" 531 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(), 532 stubImportPackages); 533 if (pInfo.type().typeArguments() != null) { 534 for (TypeInfo tInfoType : pInfo.type().typeArguments()) { 535 if (tInfoType.asClassInfo() != null) { 536 ClassInfo tcl = tInfoType.asClassInfo(); 537 if (tcl.isHiddenOrRemoved()) { 538 Errors 539 .error(Errors.UNAVAILABLE_SYMBOL, mInfo.position(), 540 "Parameter of hidden type " + tInfoType.fullName() + " in " 541 + mInfo.containingClass().qualifiedName() + '.' + mInfo.name() 542 + "()"); 543 } else { 544 cantStripThis(tcl, notStrippable, "10:" 545 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(), 546 stubImportPackages); 547 } 548 } 549 } 550 } 551 } 552 } 553 } 554 for (ClassInfo thrown : mInfo.thrownExceptions()) { 555 cantStripThis(thrown, notStrippable, "11:" + mInfo.realContainingClass().qualifiedName() 556 + ":" + mInfo.name(), stubImportPackages); 557 } 558 if (mInfo.returnType() != null && mInfo.returnType().asClassInfo() != null) { 559 cantStripThis(mInfo.returnType().asClassInfo(), notStrippable, "12:" 560 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(), 561 stubImportPackages); 562 if (mInfo.returnType().typeArguments() != null) { 563 for (TypeInfo tyInfo : mInfo.returnType().typeArguments()) { 564 if (tyInfo.asClassInfo() != null) { 565 cantStripThis(tyInfo.asClassInfo(), notStrippable, "13:" 566 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(), 567 stubImportPackages); 568 } 569 } 570 } 571 } 572 } 573 } 574 } 575 576 static String javaFileName(ClassInfo cl) { 577 String dir = ""; 578 PackageInfo pkg = cl.containingPackage(); 579 if (pkg != null) { 580 dir = pkg.name(); 581 dir = dir.replace('.', '/') + '/'; 582 } 583 return dir + cl.name() + ".java"; 584 } 585 586 static void writeClassFile(String stubsDir, HashSet<ClassInfo> notStrippable, ClassInfo cl) { 587 // inner classes are written by their containing class 588 if (cl.containingClass() != null) { 589 return; 590 } 591 592 // Work around the bogus "Array" class we invent for 593 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505) 594 if (cl.containingPackage() != null 595 && cl.containingPackage().name().equals(PackageInfo.DEFAULT_PACKAGE)) { 596 return; 597 } 598 599 String filename = stubsDir + '/' + javaFileName(cl); 600 File file = new File(filename); 601 ClearPage.ensureDirectory(file); 602 603 PrintStream stream = null; 604 try { 605 stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(file))); 606 writeClassFile(stream, notStrippable, cl); 607 } catch (FileNotFoundException e) { 608 System.err.println("error writing file: " + filename); 609 } finally { 610 if (stream != null) { 611 stream.close(); 612 } 613 } 614 } 615 616 static void writeClassFile(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) { 617 PackageInfo pkg = cl.containingPackage(); 618 if (cl.containingClass() == null) { 619 stream.print(parseLicenseHeader(cl.position())); 620 } 621 if (pkg != null) { 622 stream.println("package " + pkg.name() + ";"); 623 } 624 writeClass(stream, notStrippable, cl); 625 } 626 627 private static String parseLicenseHeader(/* @Nonnull */ SourcePositionInfo positionInfo) { 628 if (positionInfo == null) { 629 throw new NullPointerException("positionInfo == null"); 630 } 631 632 try { 633 final File sourceFile = new File(positionInfo.file); 634 if (!sourceFile.exists()) { 635 throw new IllegalArgumentException("Unable to find " + sourceFile + 636 ". This is usually because doclava has been asked to generate stubs for a file " + 637 "that isn't present in the list of input source files but exists in the input " + 638 "classpath."); 639 } 640 return parseLicenseHeader(new FileInputStream(sourceFile)); 641 } catch (IOException ioe) { 642 throw new RuntimeException("Unable to parse license header for: " + positionInfo.file, ioe); 643 } 644 } 645 646 /* @VisibleForTesting */ 647 static String parseLicenseHeader(InputStream input) throws IOException { 648 StringBuilder builder = new StringBuilder(8192); 649 try (Scanner scanner = new Scanner( 650 new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)))) { 651 String line; 652 while (scanner.hasNextLine()) { 653 line = scanner.nextLine().trim(); 654 // Use an extremely simple strategy for parsing license headers : assume that 655 // all file content before the first "package " or "import " directive is a license 656 // header. In some cases this might contain more than just the license header, but we 657 // don't care. 658 if (line.startsWith("package ") || line.startsWith("import ")) { 659 break; 660 } 661 builder.append(line); 662 builder.append("\n"); 663 } 664 665 // We've reached the end of the file without reaching any package or import 666 // directives. 667 if (!scanner.hasNextLine()) { 668 throw new IOException("Unable to parse license header"); 669 } 670 } 671 672 return builder.toString(); 673 } 674 675 static void writeClass(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) { 676 writeAnnotations(stream, cl.annotations(), cl.isDeprecated()); 677 678 stream.print(cl.scope() + " "); 679 if (cl.isAbstract() && !cl.isAnnotation() && !cl.isInterface()) { 680 stream.print("abstract "); 681 } 682 if (cl.isStatic()) { 683 stream.print("static "); 684 } 685 if (cl.isFinal() && !cl.isEnum()) { 686 stream.print("final "); 687 } 688 if (false) { 689 stream.print("strictfp "); 690 } 691 692 HashSet<String> classDeclTypeVars = new HashSet(); 693 String leafName = cl.asTypeInfo().fullName(classDeclTypeVars); 694 int bracket = leafName.indexOf('<'); 695 if (bracket < 0) bracket = leafName.length() - 1; 696 int period = leafName.lastIndexOf('.', bracket); 697 if (period < 0) period = -1; 698 leafName = leafName.substring(period + 1); 699 700 String kind = cl.kind(); 701 stream.println(kind + " " + leafName); 702 703 TypeInfo base = cl.superclassType(); 704 705 if (!"enum".equals(kind)) { 706 if (base != null && !"java.lang.Object".equals(base.qualifiedTypeName())) { 707 stream.println(" extends " + base.fullName(classDeclTypeVars)); 708 } 709 } 710 711 List<TypeInfo> usedInterfaces = new ArrayList<TypeInfo>(); 712 for (TypeInfo iface : cl.realInterfaceTypes()) { 713 if (notStrippable.contains(iface.asClassInfo()) && !iface.asClassInfo().isDocOnly()) { 714 usedInterfaces.add(iface); 715 } 716 } 717 if (usedInterfaces.size() > 0 && !cl.isAnnotation()) { 718 // can java annotations extend other ones? 719 if (cl.isInterface() || cl.isAnnotation()) { 720 stream.print(" extends "); 721 } else { 722 stream.print(" implements "); 723 } 724 String comma = ""; 725 for (TypeInfo iface : usedInterfaces) { 726 stream.print(comma + iface.fullName(classDeclTypeVars)); 727 comma = ", "; 728 } 729 stream.println(); 730 } 731 732 stream.println("{"); 733 734 ArrayList<FieldInfo> enumConstants = cl.enumConstants(); 735 int N = enumConstants.size(); 736 int i = 0; 737 for (FieldInfo field : enumConstants) { 738 writeAnnotations(stream, field.annotations(), field.isDeprecated()); 739 if (!field.constantLiteralValue().equals("null")) { 740 stream.println(field.name() + "(" + field.constantLiteralValue() 741 + (i == N - 1 ? ");" : "),")); 742 } else { 743 stream.println(field.name() + "(" + (i == N - 1 ? ");" : "),")); 744 } 745 i++; 746 } 747 748 for (ClassInfo inner : cl.getRealInnerClasses()) { 749 if (notStrippable.contains(inner) && !inner.isDocOnly()) { 750 writeClass(stream, notStrippable, inner); 751 } 752 } 753 754 755 for (MethodInfo method : cl.constructors()) { 756 if (!method.isDocOnly()) { 757 writeMethod(stream, method, true); 758 } 759 } 760 761 boolean fieldNeedsInitialization = false; 762 boolean staticFieldNeedsInitialization = false; 763 for (FieldInfo field : cl.selfFields()) { 764 if (!field.isDocOnly()) { 765 if (!field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) { 766 fieldNeedsInitialization = true; 767 } 768 if (field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) { 769 staticFieldNeedsInitialization = true; 770 } 771 } 772 } 773 774 // The compiler includes a default public constructor that calls the super classes 775 // default constructor in the case where there are no written constructors. 776 // So, if we hide all the constructors, java may put in a constructor 777 // that calls a nonexistent super class constructor. So, if there are no constructors, 778 // and the super class doesn't have a default constructor, write in a private constructor 779 // that works. TODO -- we generate this as protected, but we really should generate 780 // it as private unless it also exists in the real code. 781 if ((cl.constructors().isEmpty() && (!cl.getNonWrittenConstructors().isEmpty() || 782 fieldNeedsInitialization)) && !cl.isAnnotation() && !cl.isInterface() && !cl.isEnum()) { 783 // Errors.error(Errors.HIDDEN_CONSTRUCTOR, 784 // cl.position(), "No constructors " + 785 // "found and superclass has no parameterless constructor. A constructor " + 786 // "that calls an appropriate superclass constructor " + 787 // "was automatically written to stubs.\n"); 788 stream.println(cl.leafName() + "() { " + superCtorCall(cl, null) + "throw new" 789 + " RuntimeException(\"Stub!\"); }"); 790 } 791 792 for (MethodInfo method : cl.allSelfMethods()) { 793 if (cl.isEnum()) { 794 if (("values".equals(method.name()) && "()".equals(method.signature())) || 795 ("valueOf".equals(method.name()) && 796 "(java.lang.String)".equals(method.signature()))) { 797 // skip these two methods on enums, because they're synthetic, 798 // although for some reason javadoc doesn't mark them as synthetic, 799 // maybe because they still want them documented 800 continue; 801 } 802 } 803 if (!method.isDocOnly()) { 804 writeMethod(stream, method, false); 805 } 806 } 807 // Write all methods that are hidden or removed, but override abstract methods or interface methods. 808 // These can't be hidden. 809 List<MethodInfo> hiddenAndRemovedMethods = cl.getHiddenMethods(); 810 hiddenAndRemovedMethods.addAll(cl.getRemovedMethods()); 811 for (MethodInfo method : hiddenAndRemovedMethods) { 812 MethodInfo overriddenMethod = 813 method.findRealOverriddenMethod(method.name(), method.signature(), notStrippable); 814 ClassInfo classContainingMethod = 815 method.findRealOverriddenClass(method.name(), method.signature()); 816 if (overriddenMethod != null && !overriddenMethod.isHiddenOrRemoved() && 817 !overriddenMethod.isDocOnly() && 818 (overriddenMethod.isAbstract() || overriddenMethod.containingClass().isInterface())) { 819 method.setReason("1:" + classContainingMethod.qualifiedName()); 820 cl.addMethod(method); 821 writeMethod(stream, method, false); 822 } 823 } 824 825 for (MethodInfo element : cl.annotationElements()) { 826 if (!element.isDocOnly()) { 827 writeAnnotationElement(stream, element); 828 } 829 } 830 831 for (FieldInfo field : cl.selfFields()) { 832 if (!field.isDocOnly()) { 833 writeField(stream, field); 834 } 835 } 836 837 if (staticFieldNeedsInitialization) { 838 stream.print("static { "); 839 for (FieldInfo field : cl.selfFields()) { 840 if (!field.isDocOnly() && field.isStatic() && field.isFinal() && !fieldIsInitialized(field) 841 && field.constantValue() == null) { 842 stream.print(field.name() + " = " + field.type().defaultValue() + "; "); 843 } 844 } 845 stream.println("}"); 846 } 847 848 stream.println("}"); 849 } 850 851 static void writeMethod(PrintStream stream, MethodInfo method, boolean isConstructor) { 852 String comma; 853 854 writeAnnotations(stream, method.annotations(), method.isDeprecated()); 855 856 if (method.isDefault()) { 857 stream.print("default "); 858 } 859 stream.print(method.scope() + " "); 860 if (method.isStatic()) { 861 stream.print("static "); 862 } 863 if (method.isFinal()) { 864 stream.print("final "); 865 } 866 if (method.isAbstract()) { 867 stream.print("abstract "); 868 } 869 if (method.isSynchronized()) { 870 stream.print("synchronized "); 871 } 872 if (method.isNative()) { 873 stream.print("native "); 874 } 875 if (false /* method.isStictFP() */) { 876 stream.print("strictfp "); 877 } 878 879 stream.print(method.typeArgumentsName(new HashSet()) + " "); 880 881 if (!isConstructor) { 882 stream.print(method.returnType().fullName(method.typeVariables()) + " "); 883 } 884 String n = method.name(); 885 int pos = n.lastIndexOf('.'); 886 if (pos >= 0) { 887 n = n.substring(pos + 1); 888 } 889 stream.print(n + "("); 890 comma = ""; 891 int count = 1; 892 int size = method.parameters().size(); 893 for (ParameterInfo param : method.parameters()) { 894 stream.print(comma); 895 writeAnnotations(stream, param.annotations(), false); 896 stream.print(fullParameterTypeName(method, param.type(), count == size) + " " 897 + param.name()); 898 comma = ", "; 899 count++; 900 } 901 stream.print(")"); 902 903 comma = ""; 904 if (method.thrownExceptions().size() > 0) { 905 stream.print(" throws "); 906 for (ClassInfo thrown : method.thrownExceptions()) { 907 stream.print(comma + thrown.qualifiedName()); 908 comma = ", "; 909 } 910 } 911 if (method.isAbstract() || method.isNative() || (method.containingClass().isInterface() && (!method.isDefault() && !method.isStatic()))) { 912 stream.println(";"); 913 } else { 914 stream.print(" { "); 915 if (isConstructor) { 916 stream.print(superCtorCall(method.containingClass(), method.thrownExceptions())); 917 } 918 stream.println("throw new RuntimeException(\"Stub!\"); }"); 919 } 920 } 921 922 static void writeField(PrintStream stream, FieldInfo field) { 923 writeAnnotations(stream, field.annotations(), field.isDeprecated()); 924 925 stream.print(field.scope() + " "); 926 if (field.isStatic()) { 927 stream.print("static "); 928 } 929 if (field.isFinal()) { 930 stream.print("final "); 931 } 932 if (field.isTransient()) { 933 stream.print("transient "); 934 } 935 if (field.isVolatile()) { 936 stream.print("volatile "); 937 } 938 939 stream.print(field.type().fullName()); 940 stream.print(" "); 941 stream.print(field.name()); 942 943 if (fieldIsInitialized(field)) { 944 stream.print(" = " + field.constantLiteralValue()); 945 } 946 947 stream.println(";"); 948 } 949 950 static boolean fieldIsInitialized(FieldInfo field) { 951 return (field.isFinal() && field.constantValue() != null) 952 || !field.type().dimension().equals("") || field.containingClass().isInterface(); 953 } 954 955 static boolean canCallMethod(ClassInfo from, MethodInfo m) { 956 if (m.isPublic() || m.isProtected()) { 957 return true; 958 } 959 if (m.isPackagePrivate()) { 960 String fromPkg = from.containingPackage().name(); 961 String pkg = m.containingClass().containingPackage().name(); 962 if (fromPkg.equals(pkg)) { 963 return true; 964 } 965 } 966 return false; 967 } 968 969 // call a constructor, any constructor on this class's superclass. 970 static String superCtorCall(ClassInfo cl, ArrayList<ClassInfo> thrownExceptions) { 971 ClassInfo base = cl.realSuperclass(); 972 if (base == null) { 973 return ""; 974 } 975 HashSet<String> exceptionNames = new HashSet<String>(); 976 if (thrownExceptions != null) { 977 for (ClassInfo thrown : thrownExceptions) { 978 exceptionNames.add(thrown.name()); 979 } 980 } 981 ArrayList<MethodInfo> ctors = base.constructors(); 982 MethodInfo ctor = null; 983 // bad exception indicates that the exceptions thrown by the super constructor 984 // are incompatible with the constructor we're using for the sub class. 985 Boolean badException = false; 986 for (MethodInfo m : ctors) { 987 if (canCallMethod(cl, m)) { 988 if (m.thrownExceptions() != null) { 989 for (ClassInfo thrown : m.thrownExceptions()) { 990 if (thrownExceptions != null && !exceptionNames.contains(thrown.name())) { 991 badException = true; 992 } 993 } 994 } 995 if (badException) { 996 badException = false; 997 continue; 998 } 999 // if it has no args, we're done 1000 if (m.parameters().isEmpty()) { 1001 return ""; 1002 } 1003 ctor = m; 1004 } 1005 } 1006 if (ctor != null) { 1007 String result = ""; 1008 result += "super("; 1009 ArrayList<ParameterInfo> params = ctor.parameters(); 1010 for (ParameterInfo param : params) { 1011 TypeInfo t = param.type(); 1012 if (t.isPrimitive() && t.dimension().equals("")) { 1013 String n = t.simpleTypeName(); 1014 if (("byte".equals(n) || "short".equals(n) || "int".equals(n) || "long".equals(n) 1015 || "float".equals(n) || "double".equals(n)) 1016 && t.dimension().equals("")) { 1017 result += "0"; 1018 } else if ("char".equals(n)) { 1019 result += "'\\0'"; 1020 } else if ("boolean".equals(n)) { 1021 result += "false"; 1022 } else { 1023 result += "<<unknown-" + n + ">>"; 1024 } 1025 } else { 1026 // put null in each super class method. Cast null to the correct type 1027 // to avoid collisions with other constructors. If the type is generic 1028 // don't cast it 1029 result += 1030 (!t.isTypeVariable() ? "(" + t.qualifiedTypeName() + t.dimension() + ")" : "") 1031 + "null"; 1032 } 1033 if (param != params.get(params.size()-1)) { 1034 result += ","; 1035 } 1036 } 1037 result += "); "; 1038 return result; 1039 } else { 1040 return ""; 1041 } 1042 } 1043 1044 /** 1045 * Write out the given list of annotations. If the {@code isDeprecated} 1046 * flag is true also write out a {@code @Deprecated} annotation if it did not 1047 * already appear in the list of annotations. (This covers APIs that mention 1048 * {@code @deprecated} in their documentation but fail to add 1049 * {@code @Deprecated} as an annotation. 1050 * <p> 1051 * {@code @Override} annotations are deliberately skipped. 1052 */ 1053 static void writeAnnotations(PrintStream stream, List<AnnotationInstanceInfo> annotations, 1054 boolean isDeprecated) { 1055 assert annotations != null; 1056 for (AnnotationInstanceInfo ann : annotations) { 1057 // Skip @Override annotations: the stubs do not need it and in some cases it leads 1058 // to compilation errors with the way the stubs are generated 1059 if (ann.type() != null && ann.type().qualifiedName().equals("java.lang.Override")) { 1060 continue; 1061 } 1062 if (!ann.type().isHiddenOrRemoved()) { 1063 stream.println(ann.toString()); 1064 if (isDeprecated && ann.type() != null 1065 && ann.type().qualifiedName().equals("java.lang.Deprecated")) { 1066 isDeprecated = false; // Prevent duplicate annotations 1067 } 1068 } 1069 } 1070 if (isDeprecated) { 1071 stream.println("@Deprecated"); 1072 } 1073 } 1074 1075 static void writeAnnotationElement(PrintStream stream, MethodInfo ann) { 1076 stream.print(ann.returnType().fullName()); 1077 stream.print(" "); 1078 stream.print(ann.name()); 1079 stream.print("()"); 1080 AnnotationValueInfo def = ann.defaultAnnotationElementValue(); 1081 if (def != null) { 1082 stream.print(" default "); 1083 stream.print(def.valueString()); 1084 } 1085 stream.println(";"); 1086 } 1087 1088 public static void writeXml(PrintStream xmlWriter, Collection<PackageInfo> pkgs, boolean strip) { 1089 if (strip) { 1090 Stubs.writeXml(xmlWriter, pkgs); 1091 } else { 1092 Stubs.writeXml(xmlWriter, pkgs, c -> true); 1093 } 1094 } 1095 1096 public static void writeXml(PrintStream xmlWriter, Collection<PackageInfo> pkgs, 1097 Predicate<ClassInfo> notStrippable) { 1098 1099 final PackageInfo[] packages = pkgs.toArray(new PackageInfo[pkgs.size()]); 1100 Arrays.sort(packages, PackageInfo.comparator); 1101 1102 xmlWriter.println("<api>"); 1103 for (PackageInfo pkg: packages) { 1104 writePackageXML(xmlWriter, pkg, pkg.allClasses().values(), notStrippable); 1105 } 1106 xmlWriter.println("</api>"); 1107 } 1108 1109 public static void writeXml(PrintStream xmlWriter, Collection<PackageInfo> pkgs) { 1110 HashSet<ClassInfo> allClasses = new HashSet<>(); 1111 for (PackageInfo pkg: pkgs) { 1112 allClasses.addAll(pkg.allClasses().values()); 1113 } 1114 Predicate<ClassInfo> notStrippable = allClasses::contains; 1115 writeXml(xmlWriter, pkgs, notStrippable); 1116 } 1117 1118 static void writePackageXML(PrintStream xmlWriter, PackageInfo pack, 1119 Collection<ClassInfo> classList, Predicate<ClassInfo> notStrippable) { 1120 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]); 1121 Arrays.sort(classes, ClassInfo.comparator); 1122 // Work around the bogus "Array" class we invent for 1123 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505) 1124 if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) { 1125 return; 1126 } 1127 xmlWriter.println("<package name=\"" + pack.name() + "\"\n" 1128 // + " source=\"" + pack.position() + "\"\n" 1129 + ">"); 1130 for (ClassInfo cl : classes) { 1131 writeClassXML(xmlWriter, cl, notStrippable); 1132 } 1133 xmlWriter.println("</package>"); 1134 1135 1136 } 1137 1138 static void writeClassXML(PrintStream xmlWriter, ClassInfo cl, Predicate<ClassInfo> notStrippable) { 1139 String scope = cl.scope(); 1140 String deprecatedString = ""; 1141 String declString = (cl.isInterface()) ? "interface" : "class"; 1142 if (cl.isDeprecated()) { 1143 deprecatedString = "deprecated"; 1144 } else { 1145 deprecatedString = "not deprecated"; 1146 } 1147 xmlWriter.println("<" + declString + " name=\"" + cl.name() + "\""); 1148 if (!cl.isInterface() && !cl.qualifiedName().equals("java.lang.Object")) { 1149 xmlWriter.println(" extends=\"" 1150 + ((cl.realSuperclass() == null) ? "java.lang.Object" : cl.realSuperclass() 1151 .qualifiedName()) + "\""); 1152 } 1153 xmlWriter.println(" abstract=\"" + cl.isAbstract() + "\"\n" + " static=\"" + cl.isStatic() 1154 + "\"\n" + " final=\"" + cl.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString 1155 + "\"\n" + " visibility=\"" + scope + "\"\n" 1156 // + " source=\"" + cl.position() + "\"\n" 1157 + ">"); 1158 1159 ArrayList<ClassInfo> interfaces = cl.realInterfaces(); 1160 Collections.sort(interfaces, ClassInfo.comparator); 1161 for (ClassInfo iface : interfaces) { 1162 if (notStrippable.test(iface)) { 1163 xmlWriter.println("<implements name=\"" + iface.qualifiedName() + "\">"); 1164 xmlWriter.println("</implements>"); 1165 } 1166 } 1167 1168 ArrayList<MethodInfo> constructors = cl.constructors(); 1169 Collections.sort(constructors, MethodInfo.comparator); 1170 for (MethodInfo mi : constructors) { 1171 writeConstructorXML(xmlWriter, mi); 1172 } 1173 1174 ArrayList<MethodInfo> methods = cl.allSelfMethods(); 1175 Collections.sort(methods, MethodInfo.comparator); 1176 for (MethodInfo mi : methods) { 1177 writeMethodXML(xmlWriter, mi); 1178 } 1179 1180 ArrayList<FieldInfo> fields = cl.selfFields(); 1181 Collections.sort(fields, FieldInfo.comparator); 1182 for (FieldInfo fi : fields) { 1183 writeFieldXML(xmlWriter, fi); 1184 } 1185 xmlWriter.println("</" + declString + ">"); 1186 1187 } 1188 1189 static void writeMethodXML(PrintStream xmlWriter, MethodInfo mi) { 1190 String scope = mi.scope(); 1191 1192 String deprecatedString = ""; 1193 if (mi.isDeprecated()) { 1194 deprecatedString = "deprecated"; 1195 } else { 1196 deprecatedString = "not deprecated"; 1197 } 1198 xmlWriter.println("<method name=\"" 1199 + mi.name() 1200 + "\"\n" 1201 + ((mi.returnType() != null) ? " return=\"" 1202 + makeXMLcompliant(fullParameterTypeName(mi, mi.returnType(), false)) + "\"\n" : "") 1203 + " abstract=\"" + mi.isAbstract() + "\"\n" + " native=\"" + mi.isNative() + "\"\n" 1204 + " synchronized=\"" + mi.isSynchronized() + "\"\n" + " static=\"" + mi.isStatic() + "\"\n" 1205 + " final=\"" + mi.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString + "\"\n" 1206 + " visibility=\"" + scope + "\"\n" 1207 // + " source=\"" + mi.position() + "\"\n" 1208 + ">"); 1209 1210 // write parameters in declaration order 1211 int numParameters = mi.parameters().size(); 1212 int count = 0; 1213 for (ParameterInfo pi : mi.parameters()) { 1214 count++; 1215 writeParameterXML(xmlWriter, mi, pi, count == numParameters); 1216 } 1217 1218 // but write exceptions in canonicalized order 1219 ArrayList<ClassInfo> exceptions = mi.thrownExceptions(); 1220 Collections.sort(exceptions, ClassInfo.comparator); 1221 for (ClassInfo pi : exceptions) { 1222 xmlWriter.println("<exception name=\"" + pi.name() + "\" type=\"" + pi.qualifiedName() 1223 + "\">"); 1224 xmlWriter.println("</exception>"); 1225 } 1226 xmlWriter.println("</method>"); 1227 } 1228 1229 static void writeConstructorXML(PrintStream xmlWriter, MethodInfo mi) { 1230 String scope = mi.scope(); 1231 String deprecatedString = ""; 1232 if (mi.isDeprecated()) { 1233 deprecatedString = "deprecated"; 1234 } else { 1235 deprecatedString = "not deprecated"; 1236 } 1237 xmlWriter.println("<constructor name=\"" + mi.name() + "\"\n" + " type=\"" 1238 + mi.containingClass().qualifiedName() + "\"\n" + " static=\"" + mi.isStatic() + "\"\n" 1239 + " final=\"" + mi.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString + "\"\n" 1240 + " visibility=\"" + scope + "\"\n" 1241 // + " source=\"" + mi.position() + "\"\n" 1242 + ">"); 1243 1244 int numParameters = mi.parameters().size(); 1245 int count = 0; 1246 for (ParameterInfo pi : mi.parameters()) { 1247 count++; 1248 writeParameterXML(xmlWriter, mi, pi, count == numParameters); 1249 } 1250 1251 ArrayList<ClassInfo> exceptions = mi.thrownExceptions(); 1252 Collections.sort(exceptions, ClassInfo.comparator); 1253 for (ClassInfo pi : exceptions) { 1254 xmlWriter.println("<exception name=\"" + pi.name() + "\" type=\"" + pi.qualifiedName() 1255 + "\">"); 1256 xmlWriter.println("</exception>"); 1257 } 1258 xmlWriter.println("</constructor>"); 1259 } 1260 1261 static void writeParameterXML(PrintStream xmlWriter, MethodInfo method, ParameterInfo pi, 1262 boolean isLast) { 1263 xmlWriter.println("<parameter name=\"" + pi.name() + "\" type=\"" 1264 + makeXMLcompliant(fullParameterTypeName(method, pi.type(), isLast)) + "\">"); 1265 xmlWriter.println("</parameter>"); 1266 } 1267 1268 static void writeFieldXML(PrintStream xmlWriter, FieldInfo fi) { 1269 String scope = fi.scope(); 1270 String deprecatedString = ""; 1271 if (fi.isDeprecated()) { 1272 deprecatedString = "deprecated"; 1273 } else { 1274 deprecatedString = "not deprecated"; 1275 } 1276 // need to make sure value is valid XML 1277 String value = makeXMLcompliant(fi.constantLiteralValue()); 1278 1279 String fullTypeName = makeXMLcompliant(fi.type().fullName()); 1280 1281 xmlWriter.println("<field name=\"" + fi.name() + "\"\n" + " type=\"" + fullTypeName + "\"\n" 1282 + " transient=\"" + fi.isTransient() + "\"\n" + " volatile=\"" + fi.isVolatile() + "\"\n" 1283 + (fieldIsInitialized(fi) ? " value=\"" + value + "\"\n" : "") + " static=\"" 1284 + fi.isStatic() + "\"\n" + " final=\"" + fi.isFinal() + "\"\n" + " deprecated=\"" 1285 + deprecatedString + "\"\n" + " visibility=\"" + scope + "\"\n" 1286 // + " source=\"" + fi.position() + "\"\n" 1287 + ">"); 1288 xmlWriter.println("</field>"); 1289 } 1290 1291 static String makeXMLcompliant(String s) { 1292 String returnString = ""; 1293 returnString = s.replaceAll("&", "&"); 1294 returnString = returnString.replaceAll("<", "<"); 1295 returnString = returnString.replaceAll(">", ">"); 1296 returnString = returnString.replaceAll("\"", """); 1297 returnString = returnString.replaceAll("'", "'"); 1298 return returnString; 1299 } 1300 1301 /** 1302 * Predicate that decides if the given member should be considered part of an 1303 * API surface area. To make the most accurate decision, it searches for 1304 * signals on the member, all containing classes, and all containing packages. 1305 */ 1306 public static class ApiPredicate implements Predicate<MemberInfo> { 1307 public boolean ignoreShown; 1308 public boolean ignoreRemoved; 1309 public boolean matchRemoved; 1310 1311 /** 1312 * Set if the value of {@link MemberInfo#hasShowAnnotation()} should be 1313 * ignored. That is, this predicate will assume that all encountered members 1314 * match the "shown" requirement. 1315 * <p> 1316 * This is typically useful when generating "current.txt", when no 1317 * {@link Doclava#showAnnotations} have been defined. 1318 */ 1319 public ApiPredicate setIgnoreShown(boolean ignoreShown) { 1320 this.ignoreShown = ignoreShown; 1321 return this; 1322 } 1323 1324 /** 1325 * Set if the value of {@link MemberInfo#isRemoved()} should be ignored. 1326 * That is, this predicate will assume that all encountered members match 1327 * the "removed" requirement. 1328 * <p> 1329 * This is typically useful when generating "removed.txt", when it's okay to 1330 * reference both current and removed APIs. 1331 */ 1332 public ApiPredicate setIgnoreRemoved(boolean ignoreRemoved) { 1333 this.ignoreRemoved = ignoreRemoved; 1334 return this; 1335 } 1336 1337 /** 1338 * Set what the value of {@link MemberInfo#isRemoved()} must be equal to in 1339 * order for a member to match. 1340 * <p> 1341 * This is typically useful when generating "removed.txt", when you only 1342 * want to match members that have actually been removed. 1343 */ 1344 public ApiPredicate setMatchRemoved(boolean matchRemoved) { 1345 this.matchRemoved = matchRemoved; 1346 return this; 1347 } 1348 1349 private static PackageInfo containingPackage(PackageInfo pkg) { 1350 String name = pkg.name(); 1351 final int lastDot = name.lastIndexOf('.'); 1352 if (lastDot == -1) { 1353 return null; 1354 } else { 1355 name = name.substring(0, lastDot); 1356 return Converter.obtainPackage(name); 1357 } 1358 } 1359 1360 @Override 1361 public boolean test(MemberInfo member) { 1362 boolean visible = member.isPublic() || member.isProtected(); 1363 boolean hasShowAnnotation = member.hasShowAnnotation(); 1364 boolean hidden = member.isHidden(); 1365 boolean docOnly = member.isDocOnly(); 1366 boolean removed = member.isRemoved(); 1367 1368 ClassInfo clazz = member.containingClass(); 1369 if (clazz != null) { 1370 PackageInfo pkg = clazz.containingPackage(); 1371 while (pkg != null) { 1372 hidden |= pkg.isHidden(); 1373 docOnly |= pkg.isDocOnly(); 1374 removed |= pkg.isRemoved(); 1375 pkg = containingPackage(pkg); 1376 } 1377 } 1378 while (clazz != null) { 1379 visible &= clazz.isPublic() || clazz.isProtected(); 1380 hasShowAnnotation |= clazz.hasShowAnnotation(); 1381 hidden |= clazz.isHidden(); 1382 docOnly |= clazz.isDocOnly(); 1383 removed |= clazz.isRemoved(); 1384 clazz = clazz.containingClass(); 1385 } 1386 1387 if (ignoreShown) { 1388 hasShowAnnotation = true; 1389 } 1390 if (ignoreRemoved) { 1391 removed = matchRemoved; 1392 } 1393 1394 return visible && hasShowAnnotation && !hidden && !docOnly && (removed == matchRemoved); 1395 } 1396 } 1397 1398 /** 1399 * Filter that will elide exact duplicate members that are already included 1400 * in another superclass/interfaces. 1401 */ 1402 public static class ElidingPredicate implements Predicate<MemberInfo> { 1403 private final Predicate<MemberInfo> wrapped; 1404 1405 public ElidingPredicate(Predicate<MemberInfo> wrapped) { 1406 this.wrapped = wrapped; 1407 } 1408 1409 @Override 1410 public boolean test(MemberInfo member) { 1411 // This member should be included, but if it's an exact duplicate 1412 // override then we can elide it. 1413 if (member instanceof MethodInfo) { 1414 MethodInfo method = (MethodInfo) member; 1415 if (method.returnType() != null) { // not a constructor 1416 String methodRaw = writeMethodApiWithoutDefault(method); 1417 return (method.findPredicateOverriddenMethod(new Predicate<MemberInfo>() { 1418 @Override 1419 public boolean test(MemberInfo test) { 1420 // We're looking for included and perfect signature 1421 return (wrapped.test(test) 1422 && writeMethodApiWithoutDefault((MethodInfo) test).equals(methodRaw)); 1423 } 1424 }) == null); 1425 } 1426 } 1427 return true; 1428 } 1429 } 1430 1431 public static class FilterPredicate implements Predicate<MemberInfo> { 1432 private final Predicate<MemberInfo> wrapped; 1433 1434 public FilterPredicate(Predicate<MemberInfo> wrapped) { 1435 this.wrapped = wrapped; 1436 } 1437 1438 @Override 1439 public boolean test(MemberInfo member) { 1440 if (wrapped.test(member)) { 1441 return true; 1442 } else if (member instanceof MethodInfo) { 1443 MethodInfo method = (MethodInfo) member; 1444 return method.returnType() != null && // not a constructor 1445 method.findPredicateOverriddenMethod(wrapped) != null; 1446 } else { 1447 return false; 1448 } 1449 } 1450 } 1451 1452 static void writeApi(PrintStream apiWriter, Map<PackageInfo, List<ClassInfo>> classesByPackage, 1453 Predicate<MemberInfo> filterEmit, Predicate<MemberInfo> filterReference) { 1454 for (PackageInfo pkg : classesByPackage.keySet().stream().sorted(PackageInfo.comparator) 1455 .collect(Collectors.toList())) { 1456 if (pkg.name().equals(PackageInfo.DEFAULT_PACKAGE)) continue; 1457 1458 boolean hasWrittenPackageHead = false; 1459 for (ClassInfo clazz : classesByPackage.get(pkg).stream().sorted(ClassInfo.comparator) 1460 .collect(Collectors.toList())) { 1461 hasWrittenPackageHead = writeClassApi(apiWriter, clazz, filterEmit, filterReference, 1462 hasWrittenPackageHead); 1463 } 1464 1465 if (hasWrittenPackageHead) { 1466 apiWriter.print("}\n\n"); 1467 } 1468 } 1469 } 1470 1471 static void writeDexApi(PrintStream apiWriter, Map<PackageInfo, List<ClassInfo>> classesByPackage, 1472 Predicate<MemberInfo> filterEmit) { 1473 for (PackageInfo pkg : classesByPackage.keySet().stream().sorted(PackageInfo.comparator) 1474 .collect(Collectors.toList())) { 1475 if (pkg.name().equals(PackageInfo.DEFAULT_PACKAGE)) continue; 1476 1477 for (ClassInfo clazz : classesByPackage.get(pkg).stream().sorted(ClassInfo.comparator) 1478 .collect(Collectors.toList())) { 1479 writeClassDexApi(apiWriter, clazz, filterEmit); 1480 } 1481 } 1482 } 1483 1484 /** 1485 * Write the removed members of the class to removed.txt 1486 */ 1487 private static boolean writeClassApi(PrintStream apiWriter, ClassInfo cl, 1488 Predicate<MemberInfo> filterEmit, Predicate<MemberInfo> filterReference, 1489 boolean hasWrittenPackageHead) { 1490 1491 List<MethodInfo> constructors = cl.getExhaustiveConstructors().stream().filter(filterEmit) 1492 .sorted(MethodInfo.comparator).collect(Collectors.toList()); 1493 List<MethodInfo> methods = cl.getExhaustiveMethods().stream().filter(filterEmit) 1494 .sorted(MethodInfo.comparator).collect(Collectors.toList()); 1495 List<FieldInfo> enums = cl.getExhaustiveEnumConstants().stream().filter(filterEmit) 1496 .sorted(FieldInfo.comparator).collect(Collectors.toList()); 1497 List<FieldInfo> fields = cl.filteredFields(filterEmit).stream() 1498 .sorted(FieldInfo.comparator).collect(Collectors.toList()); 1499 1500 final boolean classEmpty = (constructors.isEmpty() && methods.isEmpty() && enums.isEmpty() 1501 && fields.isEmpty()); 1502 final boolean emit; 1503 if (filterEmit.test(cl.asMemberInfo())) { 1504 emit = true; 1505 } else if (!classEmpty) { 1506 emit = filterReference.test(cl.asMemberInfo()); 1507 } else { 1508 emit = false; 1509 } 1510 if (!emit) { 1511 return hasWrittenPackageHead; 1512 } 1513 1514 // Look for Android @SystemApi exposed outside the normal SDK; we require 1515 // that they're protected with a system permission. 1516 if (Doclava.android && Doclava.showAnnotations.contains("android.annotation.SystemApi")) { 1517 boolean systemService = "android.content.pm.PackageManager".equals(cl.qualifiedName()); 1518 for (AnnotationInstanceInfo a : cl.annotations()) { 1519 if (a.type().qualifiedNameMatches("android", "annotation.SystemService")) { 1520 systemService = true; 1521 } 1522 } 1523 if (systemService) { 1524 for (MethodInfo mi : methods) { 1525 checkSystemPermissions(mi); 1526 } 1527 } 1528 } 1529 1530 for (MethodInfo method : methods) { 1531 checkHiddenTypes(method, filterReference); 1532 } 1533 for (FieldInfo field : fields) { 1534 checkHiddenTypes(field, filterReference); 1535 } 1536 1537 if (!hasWrittenPackageHead) { 1538 hasWrittenPackageHead = true; 1539 apiWriter.print("package "); 1540 apiWriter.print(cl.containingPackage().qualifiedName()); 1541 apiWriter.print(" {\n\n"); 1542 } 1543 1544 apiWriter.print(" "); 1545 apiWriter.print(cl.scope()); 1546 if (cl.isStatic()) { 1547 apiWriter.print(" static"); 1548 } 1549 if (cl.isFinal()) { 1550 apiWriter.print(" final"); 1551 } 1552 if (cl.isAbstract()) { 1553 apiWriter.print(" abstract"); 1554 } 1555 if (cl.isDeprecated()) { 1556 apiWriter.print(" deprecated"); 1557 } 1558 apiWriter.print(" "); 1559 apiWriter.print(cl.isInterface() ? "interface" : "class"); 1560 apiWriter.print(" "); 1561 apiWriter.print(cl.name()); 1562 if (cl.hasTypeParameters()) { 1563 apiWriter.print(TypeInfo.typeArgumentsName(cl.asTypeInfo().typeArguments(), 1564 new HashSet<String>())); 1565 } 1566 1567 if (!cl.isInterface() 1568 && !"java.lang.Object".equals(cl.qualifiedName())) { 1569 final ClassInfo superclass = cl.filteredSuperclass(filterReference); 1570 if (superclass != null && !"java.lang.Object".equals(superclass.qualifiedName())) { 1571 apiWriter.print(" extends "); 1572 apiWriter.print(superclass.qualifiedName()); 1573 } 1574 } 1575 1576 List<ClassInfo> interfaces = cl.filteredInterfaces(filterReference).stream() 1577 .sorted(ClassInfo.comparator).collect(Collectors.toList()); 1578 boolean first = true; 1579 for (ClassInfo iface : interfaces) { 1580 if (first) { 1581 apiWriter.print(" implements"); 1582 first = false; 1583 } 1584 apiWriter.print(" "); 1585 apiWriter.print(iface.qualifiedName()); 1586 } 1587 1588 apiWriter.print(" {\n"); 1589 1590 for (MethodInfo mi : constructors) { 1591 writeConstructorApi(apiWriter, mi); 1592 } 1593 for (MethodInfo mi : methods) { 1594 writeMethodApi(apiWriter, mi); 1595 } 1596 for (FieldInfo fi : enums) { 1597 writeFieldApi(apiWriter, fi, "enum_constant"); 1598 } 1599 for (FieldInfo fi : fields) { 1600 writeFieldApi(apiWriter, fi, "field"); 1601 } 1602 1603 apiWriter.print(" }\n\n"); 1604 return hasWrittenPackageHead; 1605 } 1606 1607 private static void writeClassDexApi(PrintStream apiWriter, ClassInfo cl, 1608 Predicate<MemberInfo> filterEmit) { 1609 if (filterEmit.test(cl.asMemberInfo())) { 1610 apiWriter.print(toSlashFormat(cl.qualifiedName())); 1611 apiWriter.print("\n"); 1612 } 1613 1614 List<MethodInfo> constructors = cl.getExhaustiveConstructors().stream().filter(filterEmit) 1615 .sorted(MethodInfo.comparator).collect(Collectors.toList()); 1616 List<MethodInfo> methods = cl.getExhaustiveMethods().stream().filter(filterEmit) 1617 .sorted(MethodInfo.comparator).collect(Collectors.toList()); 1618 List<FieldInfo> enums = cl.getExhaustiveEnumConstants().stream().filter(filterEmit) 1619 .sorted(FieldInfo.comparator).collect(Collectors.toList()); 1620 List<FieldInfo> fields = cl.getExhaustiveFields().stream().filter(filterEmit) 1621 .sorted(FieldInfo.comparator).collect(Collectors.toList()); 1622 1623 for (MethodInfo mi : constructors) { 1624 writeMethodDexApi(apiWriter, cl, mi); 1625 } 1626 for (MethodInfo mi : methods) { 1627 writeMethodDexApi(apiWriter, cl, mi); 1628 } 1629 for (FieldInfo fi : enums) { 1630 writeFieldDexApi(apiWriter, cl, fi); 1631 } 1632 for (FieldInfo fi : fields) { 1633 writeFieldDexApi(apiWriter, cl, fi); 1634 } 1635 } 1636 1637 private static void checkSystemPermissions(MethodInfo mi) { 1638 boolean hasAnnotation = false; 1639 for (AnnotationInstanceInfo a : mi.annotations()) { 1640 if (a.type().qualifiedNameMatches("android", "annotation.RequiresPermission")) { 1641 hasAnnotation = true; 1642 for (AnnotationValueInfo val : a.elementValues()) { 1643 ArrayList<AnnotationValueInfo> values = new ArrayList<>(); 1644 boolean any = false; 1645 switch (val.element().name()) { 1646 case "value": 1647 values.add(val); 1648 break; 1649 case "allOf": 1650 values = (ArrayList<AnnotationValueInfo>) val.value(); 1651 break; 1652 case "anyOf": 1653 any = true; 1654 values = (ArrayList<AnnotationValueInfo>) val.value(); 1655 break; 1656 } 1657 if (values.isEmpty()) continue; 1658 1659 ArrayList<String> system = new ArrayList<>(); 1660 ArrayList<String> nonSystem = new ArrayList<>(); 1661 for (AnnotationValueInfo value : values) { 1662 final String perm = String.valueOf(value.value()); 1663 final String level = Doclava.manifestPermissions.getOrDefault(perm, null); 1664 if (level == null) { 1665 Errors.error(Errors.REMOVED_FIELD, mi.position(), 1666 "Permission '" + perm + "' is not defined by AndroidManifest.xml."); 1667 continue; 1668 } 1669 if (level.contains("normal") || level.contains("dangerous") 1670 || level.contains("ephemeral")) { 1671 nonSystem.add(perm); 1672 } else { 1673 system.add(perm); 1674 } 1675 } 1676 1677 if (system.isEmpty() && nonSystem.isEmpty()) { 1678 hasAnnotation = false; 1679 } else if ((any && !nonSystem.isEmpty()) || (!any && system.isEmpty())) { 1680 Errors.error(Errors.REQUIRES_PERMISSION, mi, "Method '" + mi.name() 1681 + "' must be protected with a system permission; it currently" 1682 + " allows non-system callers holding " + nonSystem.toString()); 1683 } 1684 } 1685 } 1686 } 1687 if (!hasAnnotation) { 1688 Errors.error(Errors.REQUIRES_PERMISSION, mi, "Method '" + mi.name() 1689 + "' must be protected with a system permission."); 1690 } 1691 } 1692 1693 private static void checkHiddenTypes(MethodInfo method, Predicate<MemberInfo> filterReference) { 1694 checkHiddenTypes(method.returnType(), method, filterReference); 1695 List<ParameterInfo> params = method.parameters(); 1696 if (params != null) { 1697 for (ParameterInfo param : params) { 1698 checkHiddenTypes(param.type(), method, filterReference); 1699 } 1700 } 1701 } 1702 1703 private static void checkHiddenTypes(FieldInfo field, Predicate<MemberInfo> filterReference) { 1704 checkHiddenTypes(field.type(), field, filterReference); 1705 } 1706 1707 private static void checkHiddenTypes(TypeInfo type, MemberInfo member, 1708 Predicate<MemberInfo> filterReference) { 1709 if (type == null || type.isPrimitive()) { 1710 return; 1711 } 1712 1713 ClassInfo clazz = type.asClassInfo(); 1714 if (clazz == null || !filterReference.test(clazz.asMemberInfo())) { 1715 Errors.error(Errors.HIDDEN_TYPE_PARAMETER, member.position(), 1716 "Member " + member + " references hidden type " + type.qualifiedTypeName() + "."); 1717 } 1718 1719 List<TypeInfo> args = type.typeArguments(); 1720 if (args != null) { 1721 for (TypeInfo arg : args) { 1722 checkHiddenTypes(arg, member, filterReference); 1723 } 1724 } 1725 } 1726 1727 static void writeConstructorApi(PrintStream apiWriter, MethodInfo mi) { 1728 apiWriter.print(" ctor "); 1729 apiWriter.print(mi.scope()); 1730 if (mi.isDeprecated()) { 1731 apiWriter.print(" deprecated"); 1732 } 1733 apiWriter.print(" "); 1734 apiWriter.print(mi.name()); 1735 1736 writeParametersApi(apiWriter, mi, mi.parameters()); 1737 writeThrowsApi(apiWriter, mi.thrownExceptions()); 1738 apiWriter.print(";\n"); 1739 } 1740 1741 static String writeMethodApiWithoutDefault(MethodInfo mi) { 1742 final ByteArrayOutputStream out = new ByteArrayOutputStream(); 1743 writeMethodApi(new PrintStream(out), mi, false); 1744 return out.toString(); 1745 } 1746 1747 static void writeMethodApi(PrintStream apiWriter, MethodInfo mi) { 1748 writeMethodApi(apiWriter, mi, true); 1749 } 1750 1751 static void writeMethodApi(PrintStream apiWriter, MethodInfo mi, boolean withDefault) { 1752 apiWriter.print(" method "); 1753 apiWriter.print(mi.scope()); 1754 if (mi.isDefault() && withDefault) { 1755 apiWriter.print(" default"); 1756 } 1757 if (mi.isStatic()) { 1758 apiWriter.print(" static"); 1759 } 1760 if (mi.isFinal()) { 1761 apiWriter.print(" final"); 1762 } 1763 if (mi.isAbstract()) { 1764 apiWriter.print(" abstract"); 1765 } 1766 if (mi.isDeprecated()) { 1767 apiWriter.print(" deprecated"); 1768 } 1769 if (mi.isSynchronized()) { 1770 apiWriter.print(" synchronized"); 1771 } 1772 if (mi.hasTypeParameters()) { 1773 apiWriter.print(" " + mi.typeArgumentsName(new HashSet<String>())); 1774 } 1775 apiWriter.print(" "); 1776 if (mi.returnType() == null) { 1777 apiWriter.print("void"); 1778 } else { 1779 apiWriter.print(fullParameterTypeName(mi, mi.returnType(), false)); 1780 } 1781 apiWriter.print(" "); 1782 apiWriter.print(mi.name()); 1783 1784 writeParametersApi(apiWriter, mi, mi.parameters()); 1785 writeThrowsApi(apiWriter, mi.thrownExceptions()); 1786 1787 apiWriter.print(";\n"); 1788 } 1789 1790 static void writeMethodDexApi(PrintStream apiWriter, ClassInfo cl, MethodInfo mi) { 1791 apiWriter.print(toSlashFormat(cl.qualifiedName())); 1792 apiWriter.print("->"); 1793 if (mi.returnType() == null) { 1794 apiWriter.print("<init>"); 1795 } else { 1796 apiWriter.print(mi.name()); 1797 } 1798 writeParametersDexApi(apiWriter, mi, mi.parameters()); 1799 if (mi.returnType() == null) { // constructor 1800 apiWriter.print("V"); 1801 } else { 1802 apiWriter.print(toSlashFormat(mi.returnType().dexName())); 1803 } 1804 apiWriter.print("\n"); 1805 } 1806 1807 static void writeParametersApi(PrintStream apiWriter, MethodInfo method, 1808 ArrayList<ParameterInfo> params) { 1809 apiWriter.print("("); 1810 1811 for (ParameterInfo pi : params) { 1812 if (pi != params.get(0)) { 1813 apiWriter.print(", "); 1814 } 1815 apiWriter.print(fullParameterTypeName(method, pi.type(), pi == params.get(params.size()-1))); 1816 // turn on to write the names too 1817 if (false) { 1818 apiWriter.print(" "); 1819 apiWriter.print(pi.name()); 1820 } 1821 } 1822 1823 apiWriter.print(")"); 1824 } 1825 1826 static void writeParametersDexApi(PrintStream apiWriter, MethodInfo method, 1827 ArrayList<ParameterInfo> params) { 1828 apiWriter.print("("); 1829 for (ParameterInfo pi : params) { 1830 String typeName = pi.type().dexName(); 1831 if (method.isVarArgs() && pi == params.get(params.size() - 1)) { 1832 typeName += "[]"; 1833 } 1834 apiWriter.print(toSlashFormat(typeName)); 1835 } 1836 apiWriter.print(")"); 1837 } 1838 1839 static void writeThrowsApi(PrintStream apiWriter, ArrayList<ClassInfo> exceptions) { 1840 // write in a canonical order 1841 exceptions = (ArrayList<ClassInfo>) exceptions.clone(); 1842 Collections.sort(exceptions, ClassInfo.comparator); 1843 //final int N = exceptions.length; 1844 boolean first = true; 1845 for (ClassInfo ex : exceptions) { 1846 // Turn this off, b/c we need to regenrate the old xml files. 1847 if (true || !"java.lang.RuntimeException".equals(ex.qualifiedName()) 1848 && !ex.isDerivedFrom("java.lang.RuntimeException")) { 1849 if (first) { 1850 apiWriter.print(" throws "); 1851 first = false; 1852 } else { 1853 apiWriter.print(", "); 1854 } 1855 apiWriter.print(ex.qualifiedName()); 1856 } 1857 } 1858 } 1859 1860 static void writeFieldApi(PrintStream apiWriter, FieldInfo fi, String label) { 1861 apiWriter.print(" "); 1862 apiWriter.print(label); 1863 apiWriter.print(" "); 1864 apiWriter.print(fi.scope()); 1865 if (fi.isStatic()) { 1866 apiWriter.print(" static"); 1867 } 1868 if (fi.isFinal()) { 1869 apiWriter.print(" final"); 1870 } 1871 if (fi.isDeprecated()) { 1872 apiWriter.print(" deprecated"); 1873 } 1874 if (fi.isTransient()) { 1875 apiWriter.print(" transient"); 1876 } 1877 if (fi.isVolatile()) { 1878 apiWriter.print(" volatile"); 1879 } 1880 1881 apiWriter.print(" "); 1882 apiWriter.print(fi.type().fullName(fi.typeVariables())); 1883 1884 apiWriter.print(" "); 1885 apiWriter.print(fi.name()); 1886 1887 Object val = null; 1888 if (fi.isConstant() && fieldIsInitialized(fi)) { 1889 apiWriter.print(" = "); 1890 apiWriter.print(fi.constantLiteralValue()); 1891 val = fi.constantValue(); 1892 } 1893 1894 apiWriter.print(";"); 1895 1896 if (val != null) { 1897 if (val instanceof Integer && "char".equals(fi.type().qualifiedTypeName())) { 1898 apiWriter.format(" // 0x%04x '%s'", val, 1899 FieldInfo.javaEscapeString("" + ((char)((Integer)val).intValue()))); 1900 } else if (val instanceof Byte || val instanceof Short || val instanceof Integer) { 1901 apiWriter.format(" // 0x%x", val); 1902 } else if (val instanceof Long) { 1903 apiWriter.format(" // 0x%xL", val); 1904 } 1905 } 1906 1907 apiWriter.print("\n"); 1908 } 1909 1910 static void writeFieldDexApi(PrintStream apiWriter, ClassInfo cl, FieldInfo fi) { 1911 apiWriter.print(toSlashFormat(cl.qualifiedName())); 1912 apiWriter.print("->"); 1913 apiWriter.print(fi.name()); 1914 apiWriter.print(":"); 1915 apiWriter.print(toSlashFormat(fi.type().dexName())); 1916 apiWriter.print("\n"); 1917 } 1918 1919 static void writeKeepList(PrintStream keepListWriter, 1920 HashMap<PackageInfo, List<ClassInfo>> allClasses, HashSet<ClassInfo> notStrippable) { 1921 // extract the set of packages, sort them by name, and write them out in that order 1922 Set<PackageInfo> allClassKeys = allClasses.keySet(); 1923 PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]); 1924 Arrays.sort(allPackages, PackageInfo.comparator); 1925 1926 for (PackageInfo pack : allPackages) { 1927 writePackageKeepList(keepListWriter, pack, allClasses.get(pack), notStrippable); 1928 } 1929 } 1930 1931 static void writePackageKeepList(PrintStream keepListWriter, PackageInfo pack, 1932 Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) { 1933 // Work around the bogus "Array" class we invent for 1934 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505) 1935 if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) { 1936 return; 1937 } 1938 1939 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]); 1940 Arrays.sort(classes, ClassInfo.comparator); 1941 for (ClassInfo cl : classes) { 1942 writeClassKeepList(keepListWriter, cl, notStrippable); 1943 } 1944 } 1945 1946 static void writeClassKeepList(PrintStream keepListWriter, ClassInfo cl, 1947 HashSet<ClassInfo> notStrippable) { 1948 keepListWriter.print("-keep class "); 1949 keepListWriter.print(to$Class(cl.qualifiedName())); 1950 1951 keepListWriter.print(" {\n"); 1952 1953 ArrayList<MethodInfo> constructors = cl.constructors(); 1954 Collections.sort(constructors, MethodInfo.comparator); 1955 for (MethodInfo mi : constructors) { 1956 writeConstructorKeepList(keepListWriter, mi); 1957 } 1958 1959 keepListWriter.print("\n"); 1960 1961 ArrayList<MethodInfo> methods = cl.allSelfMethods(); 1962 Collections.sort(methods, MethodInfo.comparator); 1963 for (MethodInfo mi : methods) { 1964 // allSelfMethods is the non-hidden and visible methods. See Doclava.checkLevel. 1965 writeMethodKeepList(keepListWriter, mi); 1966 } 1967 1968 keepListWriter.print("\n"); 1969 1970 ArrayList<FieldInfo> enums = cl.enumConstants(); 1971 Collections.sort(enums, FieldInfo.comparator); 1972 for (FieldInfo fi : enums) { 1973 writeFieldKeepList(keepListWriter, fi); 1974 } 1975 1976 keepListWriter.print("\n"); 1977 1978 ArrayList<FieldInfo> fields = cl.selfFields(); 1979 Collections.sort(fields, FieldInfo.comparator); 1980 for (FieldInfo fi : fields) { 1981 writeFieldKeepList(keepListWriter, fi); 1982 } 1983 1984 keepListWriter.print("}\n\n"); 1985 } 1986 1987 static void writeConstructorKeepList(PrintStream keepListWriter, MethodInfo mi) { 1988 keepListWriter.print(" "); 1989 keepListWriter.print("<init>"); 1990 1991 writeParametersKeepList(keepListWriter, mi, mi.parameters()); 1992 keepListWriter.print(";\n"); 1993 } 1994 1995 static void writeMethodKeepList(PrintStream keepListWriter, MethodInfo mi) { 1996 keepListWriter.print(" "); 1997 keepListWriter.print(mi.scope()); 1998 if (mi.isStatic()) { 1999 keepListWriter.print(" static"); 2000 } 2001 if (mi.isAbstract()) { 2002 keepListWriter.print(" abstract"); 2003 } 2004 if (mi.isSynchronized()) { 2005 keepListWriter.print(" synchronized"); 2006 } 2007 keepListWriter.print(" "); 2008 if (mi.returnType() == null) { 2009 keepListWriter.print("void"); 2010 } else { 2011 keepListWriter.print(getCleanTypeName(mi.returnType())); 2012 } 2013 keepListWriter.print(" "); 2014 keepListWriter.print(mi.name()); 2015 2016 writeParametersKeepList(keepListWriter, mi, mi.parameters()); 2017 2018 keepListWriter.print(";\n"); 2019 } 2020 2021 static void writeParametersKeepList(PrintStream keepListWriter, MethodInfo method, 2022 ArrayList<ParameterInfo> params) { 2023 keepListWriter.print("("); 2024 2025 for (ParameterInfo pi : params) { 2026 if (pi != params.get(0)) { 2027 keepListWriter.print(", "); 2028 } 2029 keepListWriter.print(getCleanTypeName(pi.type())); 2030 } 2031 2032 keepListWriter.print(")"); 2033 } 2034 2035 static void writeFieldKeepList(PrintStream keepListWriter, FieldInfo fi) { 2036 keepListWriter.print(" "); 2037 keepListWriter.print(fi.scope()); 2038 if (fi.isStatic()) { 2039 keepListWriter.print(" static"); 2040 } 2041 if (fi.isTransient()) { 2042 keepListWriter.print(" transient"); 2043 } 2044 if (fi.isVolatile()) { 2045 keepListWriter.print(" volatile"); 2046 } 2047 2048 keepListWriter.print(" "); 2049 keepListWriter.print(getCleanTypeName(fi.type())); 2050 2051 keepListWriter.print(" "); 2052 keepListWriter.print(fi.name()); 2053 2054 keepListWriter.print(";\n"); 2055 } 2056 2057 static String fullParameterTypeName(MethodInfo method, TypeInfo type, boolean isLast) { 2058 String fullTypeName = type.fullName(method.typeVariables()); 2059 if (isLast && method.isVarArgs()) { 2060 // TODO: note that this does not attempt to handle hypothetical 2061 // vararg methods whose last parameter is a list of arrays, e.g. 2062 // "Object[]...". 2063 fullTypeName = type.fullNameNoDimension(method.typeVariables()) + "..."; 2064 } 2065 return fullTypeName; 2066 } 2067 2068 static String to$Class(String name) { 2069 int pos = 0; 2070 while ((pos = name.indexOf('.', pos)) > 0) { 2071 String n = name.substring(0, pos); 2072 if (Converter.obtainClass(n) != null) { 2073 return n + (name.substring(pos).replace('.', '$')); 2074 } 2075 pos = pos + 1; 2076 } 2077 return name; 2078 } 2079 2080 static String toSlashFormat(String name) { 2081 String dimension = ""; 2082 while (name.endsWith("[]")) { 2083 dimension += "["; 2084 name = name.substring(0, name.length() - 2); 2085 } 2086 2087 final String base; 2088 if (name.equals("void")) { 2089 base = "V"; 2090 } else if (name.equals("byte")) { 2091 base = "B"; 2092 } else if (name.equals("boolean")) { 2093 base = "Z"; 2094 } else if (name.equals("char")) { 2095 base = "C"; 2096 } else if (name.equals("short")) { 2097 base = "S"; 2098 } else if (name.equals("int")) { 2099 base = "I"; 2100 } else if (name.equals("long")) { 2101 base = "J"; 2102 } else if (name.equals("float")) { 2103 base = "F"; 2104 } else if (name.equals("double")) { 2105 base = "D"; 2106 } else { 2107 base = "L" + to$Class(name).replace(".", "/") + ";"; 2108 } 2109 2110 return dimension + base; 2111 } 2112 2113 static String getCleanTypeName(TypeInfo t) { 2114 return t.isPrimitive() ? t.simpleTypeName() + t.dimension() : 2115 to$Class(t.asClassInfo().qualifiedName() + t.dimension()); 2116 } 2117 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
2020-02-15 Can not perform this action after onSaveInstanceState