diff options
Diffstat (limited to 'tools')
122 files changed, 13866 insertions, 79 deletions
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index 0fc2617bb9f2..0aaf3e8fd6c7 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -7,7 +7,9 @@ #include "AaptUtil.h" #include "Main.h" #include "ResourceFilter.h" +#include "Utils.h" +#include <androidfw/PathUtils.h> #include <utils/misc.h> #include <utils/SortedVector.h> @@ -96,7 +98,7 @@ static bool isHidden(const char *root, const char *path) char *matchedPattern = NULL; String8 fullPath(root); - fullPath.appendPath(path); + appendPath(fullPath, String8(path)); FileType type = getFileType(fullPath); int plen = strlen(path); @@ -508,7 +510,7 @@ String8 AaptFile::getPrintableSource() const { if (hasData()) { String8 name(mGroupEntry.toDirName(String8())); - name.appendPath(mPath); + appendPath(name, mPath); name.append(" #generated"); return name; } @@ -615,7 +617,7 @@ sp<AaptDir> AaptDir::makeDir(const String8& path) String8 remain = path; sp<AaptDir> subdir = this; - while (name = remain.walkPath(&remain), remain != "") { + while (name = walkPath(remain, &remain), remain != "") { subdir = subdir->makeDir(name); } @@ -623,7 +625,7 @@ sp<AaptDir> AaptDir::makeDir(const String8& path) if (i >= 0) { return subdir->mDirs.valueAt(i); } - sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name)); + sp<AaptDir> dir = new AaptDir(name, appendPathCopy(subdir->mPath, name)); subdir->mDirs.add(name, dir); return dir; } @@ -645,7 +647,7 @@ status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file, if (mFiles.indexOfKey(leafName) >= 0) { group = mFiles.valueFor(leafName); } else { - group = new AaptGroup(leafName, mPath.appendPathCopy(leafName)); + group = new AaptGroup(leafName, appendPathCopy(mPath, leafName)); mFiles.add(leafName, group); } @@ -684,7 +686,7 @@ ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir, // Add fully qualified path for dependency purposes // if we're collecting them if (fullResPaths != NULL) { - fullResPaths->add(srcDir.appendPathCopy(name)); + fullResPaths->add(appendPathCopy(srcDir, name)); } } closedir(dir); @@ -701,7 +703,7 @@ ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir, String8 pathName(srcDir); FileType type; - pathName.appendPath(fileNames[i].c_str()); + appendPath(pathName, fileNames[i]); type = getFileType(pathName.c_str()); if (type == kFileTypeDirectory) { sp<AaptDir> subdir; @@ -709,7 +711,7 @@ ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir, if (mDirs.indexOfKey(fileNames[i]) >= 0) { subdir = mDirs.valueFor(fileNames[i]); } else { - subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i])); + subdir = new AaptDir(fileNames[i], appendPathCopy(mPath, fileNames[i])); notAdded = true; } ssize_t res = subdir->slurpFullTree(bundle, pathName, kind, @@ -821,11 +823,11 @@ String8 AaptDir::getPrintableSource() const { if (mFiles.size() > 0) { // Arbitrarily pull the first file out of the list as the source dir. - return mFiles.valueAt(0)->getPrintableSource().getPathDir(); + return getPathDir(mFiles.valueAt(0)->getPrintableSource()); } if (mDirs.size() > 0) { // Or arbitrarily pull the first dir out of the list as the source dir. - return mDirs.valueAt(0)->getPrintableSource().getPathDir(); + return getPathDir(mDirs.valueAt(0)->getPrintableSource()); } // Should never hit this case, but to be safe... @@ -908,8 +910,8 @@ sp<AaptFile> AaptAssets::addFile( sp<AaptFile> file; String8 root, remain(filePath), partialPath; while (remain.length() > 0) { - root = remain.walkPath(&remain); - partialPath.appendPath(root); + root = walkPath(remain, &remain); + appendPath(partialPath, root); const String8 rootStr(root); @@ -924,7 +926,7 @@ sp<AaptFile> AaptAssets::addFile( return NULL; } } - file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType); + file = new AaptFile(appendPathCopy(srcDir, filePath), entry, resType); status_t res = group->addFile(file); if (res != NO_ERROR) { return NULL; @@ -981,7 +983,7 @@ ssize_t AaptAssets::slurpFromArgs(Bundle* bundle) if (bundle->getAndroidManifestFile() != NULL) { // place at root of zip. String8 srcFile(bundle->getAndroidManifestFile()); - addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(), + addFile(getPathLeaf(srcFile), AaptGroupEntry(), getPathDir(srcFile), NULL, String8()); totalCount++; } @@ -1154,7 +1156,7 @@ ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir) } String8 subdirName(srcDir); - subdirName.appendPath(entry->d_name); + appendPath(subdirName, entry->d_name); AaptGroupEntry group; String8 resType; @@ -1239,16 +1241,16 @@ AaptAssets::slurpResourceZip(Bundle* /* bundle */, const char* filename) String8 entryName(entry->getFileName()); - String8 dirName = entryName.getPathDir(); + String8 dirName = getPathDir(entryName); sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName); String8 resType; AaptGroupEntry kind; String8 remain; - if (entryName.walkPath(&remain) == kResourceDir) { + if (walkPath(entryName, &remain) == kResourceDir) { // these are the resources, pull their type out of the directory name - kind.initFromDirName(remain.walkPath().c_str(), &resType); + kind.initFromDirName(walkPath(remain).c_str(), &resType); } else { // these are untyped and don't have an AaptGroupEntry } @@ -1258,10 +1260,10 @@ AaptAssets::slurpResourceZip(Bundle* /* bundle */, const char* filename) } // use the one from the zip file if they both exist. - dir->removeFile(entryName.getPathLeaf()); + dir->removeFile(getPathLeaf(entryName)); sp<AaptFile> file = new AaptFile(entryName, kind, resType); - status_t err = dir->addLeafFile(entryName.getPathLeaf(), file); + status_t err = dir->addLeafFile(getPathLeaf(entryName), file); if (err != NO_ERROR) { fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.c_str()); count = err; @@ -1374,7 +1376,7 @@ status_t AaptAssets::filter(Bundle* bundle) // containing no entries. continue; } - if (file->getPath().getPathExtension() == ".xml") { + if (getPathExtension(file->getPath()) == ".xml") { // We can't remove .xml files at this point, because when // we parse them they may add identifier resources, so // removing them can cause our resource identifiers to @@ -1411,7 +1413,7 @@ status_t AaptAssets::filter(Bundle* bundle) // containing no entries. continue; } - if (file->getPath().getPathExtension() == ".xml") { + if (getPathExtension(file->getPath()) == ".xml") { // We can't remove .xml files at this point, because when // we parse them they may add identifier resources, so // removing them can cause our resource identifiers to diff --git a/tools/aapt/Android.bp b/tools/aapt/Android.bp index cecd95a5e616..68db56d58b16 100644 --- a/tools/aapt/Android.bp +++ b/tools/aapt/Android.bp @@ -51,6 +51,10 @@ cc_defaults { "libz", ], + whole_static_libs: [ + "libandroidfw_pathutils", + ], + cflags: [ "-Wall", "-Werror", diff --git a/tools/aapt/CacheUpdater.h b/tools/aapt/CacheUpdater.h index 2dc143c6a66d..dc5493f9e500 100644 --- a/tools/aapt/CacheUpdater.h +++ b/tools/aapt/CacheUpdater.h @@ -7,6 +7,7 @@ #ifndef CACHE_UPDATER_H #define CACHE_UPDATER_H +#include <androidfw/PathUtils.h> #include <utils/String8.h> #include <sys/types.h> #include <sys/stat.h> @@ -16,6 +17,8 @@ #include <direct.h> #endif +#include "Utils.h" + using namespace android; /** CacheUpdater @@ -72,14 +75,14 @@ public: do { // As we remove the end of existsPath add it to // the string of paths to create. - toCreate = existsPath.getPathLeaf().appendPath(toCreate); - existsPath = existsPath.getPathDir(); + toCreate = appendPathCopy(getPathLeaf(existsPath), toCreate); + existsPath = getPathDir(existsPath); } while (stat(existsPath.c_str(),&s) == -1); // Walk forwards and build directories as we go do { // Advance to the next segment of the path - existsPath.appendPath(toCreate.walkPath(&remains)); + appendPath(existsPath, walkPath(toCreate, &remains)); toCreate = remains; #ifdef _WIN32 _mkdir(existsPath.c_str()); @@ -101,7 +104,7 @@ public: virtual void processImage(String8 source, String8 dest) { // Make sure we're trying to write to a directory that is extant - ensureDirectoriesExist(dest.getPathDir()); + ensureDirectoriesExist(getPathDir(dest)); preProcessImageToCache(bundle, source, dest); }; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 60f3f2715395..800466aa587f 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -12,6 +12,7 @@ #include "ResourceTable.h" #include "XMLNode.h" +#include <androidfw/PathUtils.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/List.h> @@ -2486,12 +2487,12 @@ int doAdd(Bundle* bundle) for (int i = 1; i < bundle->getFileSpecCount(); i++) { const char* fileName = bundle->getFileSpecEntry(i); - if (strcasecmp(String8(fileName).getPathExtension().c_str(), ".gz") == 0) { + if (strcasecmp(getPathExtension(String8(fileName)).c_str(), ".gz") == 0) { printf(" '%s'... (from gzip)\n", fileName); - result = zip->addGzip(fileName, String8(fileName).getBasePath().c_str(), NULL); + result = zip->addGzip(fileName, getBasePath(String8(fileName)).c_str(), NULL); } else { if (bundle->getJunkPath()) { - String8 storageName = String8(fileName).getPathLeaf(); + String8 storageName = getPathLeaf(String8(fileName)); printf(" '%s' as '%s'...\n", fileName, ResTable::normalizeForOutput(storageName.c_str()).c_str()); result = zip->add(fileName, storageName.c_str(), @@ -2617,10 +2618,10 @@ static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) return original; } - String8 ext(original.getPathExtension()); + String8 ext(getPathExtension(original)); if (ext == String8(".apk")) { return String8::format("%s_%s%s", - original.getBasePath().c_str(), + getBasePath(original).c_str(), split->getDirectorySafeName().c_str(), ext.c_str()); } @@ -2756,7 +2757,7 @@ int doPackage(Bundle* bundle) // generate the dependency file in the R.java package subdirectory // e.g. gen/com/foo/app/R.java.d dependencyFile = String8(bundle->getRClassDir()); - dependencyFile.appendPath("R.java.d"); + appendPath(dependencyFile, "R.java.d"); } // Make sure we have a clean dependency file to start with fp = fopen(dependencyFile, "w"); diff --git a/tools/aapt/CrunchCache.cpp b/tools/aapt/CrunchCache.cpp index 1f2febec2230..e731ce0d5ccf 100644 --- a/tools/aapt/CrunchCache.cpp +++ b/tools/aapt/CrunchCache.cpp @@ -5,6 +5,7 @@ // This file defines functions laid out and documented in // CrunchCache.h +#include <androidfw/PathUtils.h> #include <utils/Compat.h> #include <utils/Vector.h> #include <utils/String8.h> @@ -52,15 +53,15 @@ size_t CrunchCache::crunch(CacheUpdater* cu, bool forceOverwrite) relativePath = String8(rPathPtr + offset); if (forceOverwrite || needsUpdating(relativePath)) { - cu->processImage(mSourcePath.appendPathCopy(relativePath), - mDestPath.appendPathCopy(relativePath)); + cu->processImage(appendPathCopy(mSourcePath, relativePath), + appendPathCopy(mDestPath, relativePath)); numFilesUpdated++; // crunchFile(relativePath); } // Delete this file from the source files and (if it exists) from the // dest files. mSourceFiles.removeItemsAt(0); - mDestFiles.removeItem(mDestPath.appendPathCopy(relativePath)); + mDestFiles.removeItem(appendPathCopy(mDestPath, relativePath)); } // Iterate through what's left of destFiles and delete leftovers @@ -99,7 +100,7 @@ bool CrunchCache::needsUpdating(const String8& relativePath) const // Retrieve modification dates for this file entry under the source and // cache directory trees. The vectors will return a modification date of 0 // if the file doesn't exist. - time_t sourceDate = mSourceFiles.valueFor(mSourcePath.appendPathCopy(relativePath)); - time_t destDate = mDestFiles.valueFor(mDestPath.appendPathCopy(relativePath)); + time_t sourceDate = mSourceFiles.valueFor(appendPathCopy(mSourcePath, relativePath)); + time_t destDate = mDestFiles.valueFor(appendPathCopy(mDestPath, relativePath)); return sourceDate > destDate; } diff --git a/tools/aapt/DirectoryWalker.h b/tools/aapt/DirectoryWalker.h index cea3a6eb0ecf..7f60d4db9626 100644 --- a/tools/aapt/DirectoryWalker.h +++ b/tools/aapt/DirectoryWalker.h @@ -7,6 +7,7 @@ #ifndef DIRECTORYWALKER_H #define DIRECTORYWALKER_H +#include <androidfw/PathUtils.h> #include <dirent.h> #include <sys/types.h> #include <sys/param.h> @@ -77,7 +78,7 @@ public: mEntry = *entryPtr; // Get stats - String8 fullPath = mBasePath.appendPathCopy(mEntry.d_name); + String8 fullPath = appendPathCopy(mBasePath, mEntry.d_name); stat(fullPath.c_str(),&mStats); return &mEntry; }; diff --git a/tools/aapt/FileFinder.cpp b/tools/aapt/FileFinder.cpp index a5c19806c804..69a8fa9fc6f2 100644 --- a/tools/aapt/FileFinder.cpp +++ b/tools/aapt/FileFinder.cpp @@ -5,6 +5,7 @@ // File Finder implementation. // Implementation for the functions declared and documented in FileFinder.h +#include <androidfw/PathUtils.h> #include <utils/Vector.h> #include <utils/String8.h> #include <utils/KeyedVector.h> @@ -57,7 +58,7 @@ bool SystemFileFinder::findFiles(String8 basePath, Vector<String8>& extensions, if (entry->d_name[0] == '.') // Skip hidden files and directories continue; - String8 fullPath = basePath.appendPathCopy(entryName); + String8 fullPath = appendPathCopy(basePath, entryName); // If this entry is a directory we'll recurse into it if (isDirectory(fullPath.c_str()) ) { DirectoryWalker* copy = dw->clone(); @@ -83,10 +84,10 @@ void SystemFileFinder::checkAndAddFile(const String8& path, const struct stat* s { // Loop over the extensions, checking for a match bool done = false; - String8 ext(path.getPathExtension()); + String8 ext(getPathExtension(path)); ext.toLower(); for (size_t i = 0; i < extensions.size() && !done; ++i) { - String8 ext2 = extensions[i].getPathExtension(); + String8 ext2 = getPathExtension(extensions[i]); ext2.toLower(); // Compare the extensions. If a match is found, add to storage. if (ext == ext2) { diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index c6c7e960ba2a..cd4de90f12f6 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -8,6 +8,7 @@ #include "Images.h" +#include <androidfw/PathUtils.h> #include <androidfw/ResourceTypes.h> #include <utils/ByteOrder.h> @@ -1357,7 +1358,7 @@ static bool write_png_protected(png_structp write_ptr, String8& printableName, p status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& /* assets */, const sp<AaptFile>& file, String8* /* outNewLeafName */) { - String8 ext(file->getPath().getPathExtension()); + String8 ext(getPathExtension(file->getPath())); // We currently only process PNG images. if (strcmp(ext.c_str(), ".png") != 0) { @@ -1518,7 +1519,7 @@ status_t preProcessImageToCache(const Bundle* bundle, const String8& source, con // Check to see if we're dealing with a 9-patch // If we are, process appropriately - if (source.getBasePath().getPathExtension() == ".9") { + if (getPathExtension(getBasePath(source)) == ".9") { if (do_9patch(source.c_str(), &imageInfo) != NO_ERROR) { return error; } @@ -1584,12 +1585,12 @@ status_t preProcessImageToCache(const Bundle* bundle, const String8& source, con status_t postProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets, ResourceTable* table, const sp<AaptFile>& file) { - String8 ext(file->getPath().getPathExtension()); + String8 ext(getPathExtension(file->getPath())); // At this point, now that we have all the resource data, all we need to // do is compile XML files. if (strcmp(ext.c_str(), ".xml") == 0) { - String16 resourceName(parseResourceName(file->getSourceFile().getPathLeaf())); + String16 resourceName(parseResourceName(getPathLeaf(file->getSourceFile()))); return compileXmlFile(bundle, assets, resourceName, file, table); } diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp index a7ff5fabf495..5e0f87f0dcaf 100644 --- a/tools/aapt/Package.cpp +++ b/tools/aapt/Package.cpp @@ -10,6 +10,7 @@ #include "ResourceFilter.h" #include "Utils.h" +#include <androidfw/PathUtils.h> #include <androidfw/misc.h> #include <utils/Log.h> @@ -170,7 +171,7 @@ status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet> /* anything here? */ if (zip->getNumEntries() == 0) { if (bundle->getVerbose()) { - printf("Archive is empty -- removing %s\n", outputFile.getPathLeaf().c_str()); + printf("Archive is empty -- removing %s\n", getPathLeaf(outputFile).c_str()); } delete zip; // close the file so we can remove it in Win32 zip = NULL; @@ -274,9 +275,9 @@ bool processFile(Bundle* bundle, ZipFile* zip, return true; } - if (strcasecmp(storageName.getPathExtension().c_str(), ".gz") == 0) { + if (strcasecmp(getPathExtension(storageName).c_str(), ".gz") == 0) { fromGzip = true; - storageName = storageName.getBasePath(); + storageName = getBasePath(storageName); } if (bundle->getUpdate()) { @@ -366,7 +367,7 @@ bool processFile(Bundle* bundle, ZipFile* zip, */ bool okayToCompress(Bundle* bundle, const String8& pathName) { - String8 ext = pathName.getPathExtension(); + String8 ext = getPathExtension(pathName); int i; if (ext.length() == 0) diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 4a360ed1c80e..3a198fd43d48 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -19,6 +19,8 @@ #include "WorkQueue.h" #include "XMLNode.h" +#include <androidfw/PathUtils.h> + #include <algorithm> // STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary. @@ -143,8 +145,8 @@ public: mParams.inputFlags, mParams.navigation); } mPath = "res"; - mPath.appendPath(file->getGroupEntry().toDirName(mResType)); - mPath.appendPath(leaf); + appendPath(mPath, file->getGroupEntry().toDirName(mResType)); + appendPath(mPath, leaf); mBaseName = parseResourceName(leaf); if (mBaseName == "") { fprintf(stderr, "Error: malformed resource filename %s\n", @@ -779,7 +781,7 @@ static void fullyQualifyClassName(const String8& package, const sp<XMLNode>& nod if (kIsDebug) { printf("Qualifying class '%s' to '%s'", name.c_str(), className.c_str()); } - attr->string.setTo(String16(className)); + attr->string = String16(className); } } @@ -969,7 +971,7 @@ status_t massageManifest(Bundle* bundle, ResourceTable* table, sp<XMLNode> root) return UNKNOWN_ERROR; } String8 origPackage(attr->string); - attr->string.setTo(String16(manifestPackageNameOverride)); + attr->string = String16(manifestPackageNameOverride); if (kIsDebug) { printf("Overriding package '%s' to be '%s'\n", origPackage.c_str(), manifestPackageNameOverride); @@ -1007,7 +1009,7 @@ status_t massageManifest(Bundle* bundle, ResourceTable* table, sp<XMLNode> root) XMLNode::attribute_entry* attr = child->editAttribute( String16(RESOURCES_ANDROID_NAMESPACE), String16("targetPackage")); if (attr != NULL) { - attr->string.setTo(String16(instrumentationPackageNameOverride)); + attr->string = String16(instrumentationPackageNameOverride); } } } @@ -1686,7 +1688,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil ResourceDirIterator it(fonts, String8("font")); while ((err=it.next()) == NO_ERROR) { // fonts can be resources other than xml. - if (it.getFile()->getPath().getPathExtension() == ".xml") { + if (getPathExtension(it.getFile()->getPath()) == ".xml") { String8 src = it.getFile()->getPrintableSource(); err = compileXmlFile(bundle, assets, String16(it.getBaseName()), it.getFile(), &table, xmlFlags); @@ -1716,7 +1718,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil workItem.file, &table, xmlCompilationFlags); if (err == NO_ERROR && workItem.file->hasData()) { - assets->addResource(workItem.resPath.getPathLeaf(), + assets->addResource(getPathLeaf(workItem.resPath), workItem.resPath, workItem.file, workItem.file->getResourceType()); @@ -2851,7 +2853,7 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, s++; if (s > last && (*s == '.' || *s == 0)) { String8 part(last, s-last); - dest.appendPath(part); + appendPath(dest, part); #ifdef _WIN32 _mkdir(dest.c_str()); #else @@ -2861,7 +2863,7 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, } } while (*s); } - dest.appendPath(className); + appendPath(dest, className); dest.append(".java"); FILE* fp = fopen(dest.c_str(), "w+"); if (fp == NULL) { @@ -2892,7 +2894,7 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, if (textSymbolsDest != NULL && R == className) { String8 textDest(textSymbolsDest); - textDest.appendPath(className); + appendPath(textDest, className); textDest.append(".txt"); FILE* fp = fopen(textDest.c_str(), "w+"); @@ -2918,7 +2920,7 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, if (bundle->getGenDependencies() && R == className) { // Add this R.java to the dependency file String8 dependencyFile(bundle->getRClassDir()); - dependencyFile.appendPath("R.java.d"); + appendPath(dependencyFile, "R.java.d"); FILE *fp = fopen(dependencyFile.c_str(), "a"); fprintf(fp,"%s \\\n", dest.c_str()); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index bc252cfff239..9fb731948b32 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -14,6 +14,7 @@ #include "Utils.h" #include <algorithm> +#include <androidfw/PathUtils.h> #include <androidfw/ResourceTypes.h> #include <utils/ByteOrder.h> #include <utils/TypeHelpers.h> @@ -83,7 +84,7 @@ status_t compileXmlFile(const Bundle* bundle, sp<AaptDir> resDir = assets->getDirs().valueFor(String8("res")); sp<AaptDir> dir = resDir->getDirs().valueFor(target->getGroupEntry().toDirName( target->getResourceType())); - dir->removeFile(target->getPath().getPathLeaf()); + dir->removeFile(getPathLeaf(target->getPath())); return NO_ERROR; } @@ -1363,11 +1364,11 @@ status_t compileResourceFile(Bundle* bundle, size_t length; const char16_t* attr = block.getAttributeName(i, &length); if (strcmp16(attr, name16.c_str()) == 0) { - name.setTo(block.getAttributeStringValue(i, &length)); + name = String16(block.getAttributeStringValue(i, &length)); } else if (strcmp16(attr, translatable16.c_str()) == 0) { - translatable.setTo(block.getAttributeStringValue(i, &length)); + translatable = String16(block.getAttributeStringValue(i, &length)); } else if (strcmp16(attr, formatted16.c_str()) == 0) { - formatted.setTo(block.getAttributeStringValue(i, &length)); + formatted = String16(block.getAttributeStringValue(i, &length)); } } @@ -1541,7 +1542,7 @@ status_t compileResourceFile(Bundle* bundle, } else { ssize_t sep = ident.findLast('.'); if (sep >= 0) { - parentIdent.setTo(ident, sep); + parentIdent = String16(ident, sep); } } @@ -2831,10 +2832,10 @@ ResourceTable::validateLocalizations(void) String8 config; comma = strchr(start, ','); if (comma != NULL) { - config.setTo(start, comma - start); + config = String8(start, comma - start); start = comma + 1; } else { - config.setTo(start); + config = start; } if (!locale.initFromFilterString(config)) { diff --git a/tools/aapt/Utils.cpp b/tools/aapt/Utils.cpp index 36b018e7dd2c..946916a0598a 100644 --- a/tools/aapt/Utils.cpp +++ b/tools/aapt/Utils.cpp @@ -36,3 +36,26 @@ void convertToResPath([[maybe_unused]] String8& s) { } #endif } + +String8 walkPath(const String8& path, String8* outRemains) { + const char* cp; + const char* const str = path.c_str(); + const char* buf = str; + + cp = strchr(buf, OS_PATH_SEPARATOR); + if (cp == buf) { + // don't include a leading '/'. + buf = buf + 1; + cp = strchr(buf, OS_PATH_SEPARATOR); + } + + if (cp == nullptr) { + String8 res = buf != str ? String8(buf) : path; + if (outRemains) *outRemains = String8(); + return res; + } + + String8 res(buf, cp - buf); + if (outRemains) *outRemains = String8(cp + 1); + return res; +} diff --git a/tools/aapt/Utils.h b/tools/aapt/Utils.h index 8eb594138478..f0d69799f7e2 100644 --- a/tools/aapt/Utils.h +++ b/tools/aapt/Utils.h @@ -26,3 +26,13 @@ // If the default OS separator is backslash, this converts all // backslashes to slashes, in-place. Otherwise it does nothing. void convertToResPath(android::String8&); + +/** + * Retrieve the front (root dir) component. Optionally also return the + * remaining components. + * + * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c") + * "/tmp" --> "tmp" (remain = "") + * "bar.c" --> "bar.c" (remain = "") + */ +android::String8 walkPath(const android::String8& path, android::String8* outRemains = nullptr); diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp index e270a7333295..a887ac947835 100644 --- a/tools/aapt/XMLNode.cpp +++ b/tools/aapt/XMLNode.cpp @@ -334,9 +334,9 @@ moveon: String16 spanTag; ssize_t semi = span.name.findFirst(';'); if (semi >= 0) { - spanTag.setTo(span.name.c_str(), semi); + spanTag = String16(span.name.c_str(), semi); } else { - spanTag.setTo(span.name); + spanTag = span.name; } if (strcmp16(inXml->getElementName(&len), spanTag.c_str()) != 0) { SourcePos(String8(fileName), inXml->getLineNumber()).error( @@ -393,7 +393,7 @@ moveon: // later as part of the overall type conversion. Return to the // client the raw unprocessed text. rawString.append(curString); - outString->setTo(rawString); + *outString = rawString; } return NO_ERROR; diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp index 6bf265d2e363..a2b48187c24f 100644 --- a/tools/aapt2/dump/DumpManifest.cpp +++ b/tools/aapt2/dump/DumpManifest.cpp @@ -216,9 +216,7 @@ class SupportsScreen; class ManifestExtractor { public: - - explicit ManifestExtractor(LoadedApk* apk, DumpManifestOptions& options) - : apk_(apk), options_(options) { } + explicit ManifestExtractor(LoadedApk* apk, DumpManifestOptions& options); class Element { public: @@ -509,7 +507,7 @@ class ManifestExtractor { private: std::unique_ptr<xml::XmlResource> doc_; - std::unique_ptr<CommonFeatureGroup> commonFeatureGroup_ = util::make_unique<CommonFeatureGroup>(); + std::unique_ptr<CommonFeatureGroup> commonFeatureGroup_; std::map<std::string, ConfigDescription> locales_; std::map<uint16_t, ConfigDescription> densities_; std::vector<Element*> parent_stack_; @@ -2471,6 +2469,12 @@ static void ToProto(ManifestExtractor::Element* el, pb::Badging* out_badging) { } } +// Define this constructor after the CommonFeatureGroup class definition to avoid errors with using +// std::unique_ptr on an incomplete type. +ManifestExtractor::ManifestExtractor(LoadedApk* apk, DumpManifestOptions& options) + : apk_(apk), options_(options), commonFeatureGroup_(util::make_unique<CommonFeatureGroup>()) { +} + bool ManifestExtractor::Extract(android::IDiagnostics* diag) { // Load the manifest doc_ = apk_->LoadXml("AndroidManifest.xml", diag); diff --git a/tools/hoststubgen/.gitignore b/tools/hoststubgen/.gitignore new file mode 100644 index 000000000000..6453bdef8cee --- /dev/null +++ b/tools/hoststubgen/.gitignore @@ -0,0 +1,3 @@ +out/ +*-out/ +*.log diff --git a/tools/hoststubgen/OWNERS b/tools/hoststubgen/OWNERS new file mode 100644 index 000000000000..a8c5321307d1 --- /dev/null +++ b/tools/hoststubgen/OWNERS @@ -0,0 +1,3 @@ +omakoto@google.com +jsharkey@google.com +jaggies@google.com diff --git a/tools/hoststubgen/README.md b/tools/hoststubgen/README.md new file mode 100644 index 000000000000..b0a126292063 --- /dev/null +++ b/tools/hoststubgen/README.md @@ -0,0 +1,76 @@ +# HostStubGen + +## Overview + +This directory contains tools / sample code / investigation for host side test support. + + +## Directories and files + +- `hoststubgen/` + Contains source code of the "hoststubgen" tool and relevant code + + - `framework-policy-override.txt` + This file contains "policy overrides", which allows to control what goes to stub/impl without + having to touch the target java files. This allows quicker iteration, because you can skip + having to rebuild framework.jar. + + - `src/` + + HostStubGen tool source code. + + - `annotations-src/` See `Android.bp`. + - `helper-framework-buildtime-src/` See `Android.bp`. + - `helper-framework-runtime-src/` See `Android.bp`. + - `helper-runtime-src/` See `Android.bp`. + + - `test-tiny-framework/` See `README.md` in it. + + - `test-framework` See `README.md` in it. + +- `scripts` + - `run-host-test.sh` + + Run a host side test. Use it instead of `atest` for now. (`atest` works "mostly" but it has + problems.) + + - `dump-jar.sh` + + A script to dump the content of `*.class` and `*.jar` files. + + - `run-all-tests.sh` + + Run all tests. Many tests may fail, but at least this should run til the end. + (It should print `run-all-tests.sh finished` at the end) + +## Build and run + +### Building `HostStubGen` binary + +``` +m hoststubgen +``` + +### Run the tests + +- Run all relevant tests and test scripts. Some of thests are still expected to fail, + but this should print "finished, with no unexpected failures" at the end. + + However, because some of the script it executes depend on internal file paths to Soong's + intermediate directory, some of it might fail when something changes in the build system. + + We need proper build system integration to fix them. +``` +$ ./scripts/run-all-tests.sh +``` + +- See also `README.md` in `test-*` directories. + +## TODOs, etc + + - Make sure the parent's visibility is not smaller than the member's. + +- @HostSideTestNativeSubstitutionClass should automatically add class-keep to the substitute class. + (or at least check it.) + + - The `HostStubGenTest-framework-test-host-test-lib` jar somehow contain all ASM classes? Figure out where the dependency is coming from. diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING new file mode 100644 index 000000000000..970362611f53 --- /dev/null +++ b/tools/hoststubgen/TEST_MAPPING @@ -0,0 +1,6 @@ +{ + // TODO: Change to presubmit. + "postsubmit": [ + { "name": "tiny-framework-dump-test" } + ] +} diff --git a/tools/hoststubgen/common.sh b/tools/hoststubgen/common.sh new file mode 100644 index 000000000000..b49ee39a3142 --- /dev/null +++ b/tools/hoststubgen/common.sh @@ -0,0 +1,116 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e # Exit at failure +shopt -s globstar # Enable double-star wildcards (**) + +cd "${0%/*}" # Move to the script dir + +fail() { + echo "Error: $*" 1>&2 + exit 1 +} + +# Print the arguments and then execute. +run() { + echo "Running: $*" 1>&2 + "$@" +} + +# Concatenate the second and subsequent args with the first arg as a separator. +# e.g. `join : a b c` -> prints `a:b:c` +join() { + local IFS="$1" + shift + echo "$*" +} + +abspath() { + for name in "${@}"; do + readlink -f $name + done +} + +m() { + if (( $SKIP_BUILD )) ; then + echo "Skipping build: $*" 1>&2 + return 0 + fi + run ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@" +} + +# Extract given jar files +extract() { + for f in "${@}"; do + local out=$f.ext + run rm -fr $out + run mkdir -p $out + + # It's too noisy, so only show the first few lines. + { + # Hmm unzipping kotlin jar files may produce a warning? Let's just add `|| true`... + run unzip $f -d $out || true + } |& sed -e '5,$d' + echo ' (omitting remaining output)' + + done +} + +# Find all *.java files in $1, and print them as Java class names. +# For example, if there's a file `src/com/android/test/Test.java`, and you run +# `list_all_classes_under_dir src`, then it'll print `com.android.test.Test`. +list_all_classes_under_dir() { + local dir="$1" + ( # Use a subshell, so we won't change the current directory on the caller side. + cd "$dir" + + # List the java files, but replace the slashes with dots, and remove the `.java` suffix. + ls **/*.java | sed -e 's!/!.!g' -e 's!.java$!!' + ) +} + +checkenv() { + # Make sure $ANDROID_BUILD_TOP is set. + : ${ANDROID_BUILD_TOP:?} + + # Make sure ANDROID_BUILD_TOP doesn't contain whitespace. + set ${ANDROID_BUILD_TOP} + if [[ $# != 1 ]] ; then + fail "\$ANDROID_BUILD_TOP cannot contain whitespace." + fi +} + +checkenv + +JAVAC=${JAVAC:-javac} +JAVA=${JAVA:-java} +JAR=${JAR:-jar} + +JAVAC_OPTS=${JAVAC_OPTS:--Xmaxerrs 99999 -Xlint:none} + +SOONG_INT=$ANDROID_BUILD_TOP/out/soong/.intermediates + +JUNIT_TEST_MAIN_CLASS=com.android.hoststubgen.hosthelper.HostTestSuite + +run_junit_test_jar() { + local jar="$1" + echo "Starting test: $jar ..." + run cd "${jar%/*}" + + run $JAVA $JAVA_OPTS \ + -cp $jar \ + org.junit.runner.JUnitCore \ + $main_class || return 1 + return 0 +} diff --git a/tools/hoststubgen/hoststubgen/.gitignore b/tools/hoststubgen/hoststubgen/.gitignore new file mode 100644 index 000000000000..0f384074ed7f --- /dev/null +++ b/tools/hoststubgen/hoststubgen/.gitignore @@ -0,0 +1 @@ +framework-all-stub-out
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp new file mode 100644 index 000000000000..a617876a6da0 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/Android.bp @@ -0,0 +1,303 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +// This library contains the standard hoststubgen annotations. +java_library { + name: "hoststubgen-annotations", + srcs: [ + "annotations-src/**/*.java", + ], + host_supported: true, + + // Seems like we need it to avoid circular deps. + // Copied it from "app-compat-annotations". + sdk_version: "core_current", + visibility: ["//visibility:public"], +} + +// This library contains helper classes used in the host side test environment at runtime. +// This library is _not_ specific to Android APIs. +java_library_host { + name: "hoststubgen-helper-runtime", + srcs: [ + "helper-runtime-src/**/*.java", + ], + libs: [ + "junit", + "ow2-asm", + "ow2-asm-analysis", + "ow2-asm-commons", + "ow2-asm-tree", + "ow2-asm-util", + ], + static_libs: [ + "guava", + ], + jarjar_rules: "jarjar-rules.txt", + visibility: ["//visibility:public"], +} + +// Host-side stub generator tool. +java_binary_host { + name: "hoststubgen", + main_class: "com.android.hoststubgen.Main", + srcs: ["src/**/*.kt"], + static_libs: [ + "hoststubgen-helper-runtime", + "ow2-asm", + "ow2-asm-analysis", + "ow2-asm-commons", + "ow2-asm-tree", + "ow2-asm-util", + ], + visibility: ["//visibility:public"], +} + +// File that contains the standard command line argumetns to hoststubgen. +filegroup { + name: "hoststubgen-standard-options", + srcs: [ + "hoststubgen-standard-options.txt", + ], + visibility: ["//visibility:public"], +} + +hoststubgen_common_options = "$(location hoststubgen) " + + // "--in-jar $(location :framework-all) " + + // "--policy-override-file $(location framework-policy-override.txt) " + + "@$(location :hoststubgen-standard-options) " + + + "--out-stub-jar $(location host_stub.jar) " + + "--out-impl-jar $(location host_impl.jar) " + + + // "--keep-all-classes " + // Used it for an experiment. See KeepAllClassesFilter. + "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " + + "--gen-input-dump-file $(location hoststubgen_dump.txt) " + + "" + +// Common defaults for stub generation. +// This one is not specific to Android APIs. +genrule_defaults { + name: "hoststubgen-command-defaults", + tools: ["hoststubgen"], + srcs: [ + ":hoststubgen-standard-options", + ], + // Create two jar files. + out: [ + "host_stub.jar", + "host_impl.jar", + + // Following files are created just as FYI. + "hoststubgen_keep_all.txt", + "hoststubgen_dump.txt", + ], + // visibility: ["//visibility:public"], +} + +// Generate the stub/impl from framework-all, with hidden APIs. +java_genrule_host { + name: "framework-all-hidden-api-host", + defaults: ["hoststubgen-command-defaults"], + cmd: hoststubgen_common_options + + "--in-jar $(location :framework-all) " + + "--policy-override-file $(location framework-policy-override.txt) ", + srcs: [ + ":framework-all", + "framework-policy-override.txt", + ], + visibility: ["//visibility:private"], +} + +// Extract the stub jar from "framework-all-host" for subsequent build rules. +java_genrule_host { + name: "framework-all-hidden-api-host-stub", + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-all-hidden-api-host{host_stub.jar}", + ], + out: [ + "host_stub.jar", + ], + visibility: ["//visibility:public"], +} + +// Extract the impl jar from "framework-all-host" for subsequent build rules. +java_genrule_host { + name: "framework-all-hidden-api-host-impl", + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-all-hidden-api-host{host_impl.jar}", + ], + out: [ + "host_impl.jar", + ], + visibility: ["//visibility:public"], +} + +// Generate the stub/impl from framework-all, with only public/system/test APIs, without +// hidden APIs. +java_genrule_host { + name: "framework-all-test-api-host", + defaults: ["hoststubgen-command-defaults"], + cmd: hoststubgen_common_options + + "--intersect-stub-jar $(location :android_test_stubs_current{.jar}) " + + "--in-jar $(location :framework-all) " + + "--policy-override-file $(location framework-policy-override.txt) ", + srcs: [ + ":framework-all", + ":android_test_stubs_current{.jar}", + "framework-policy-override.txt", + ], + visibility: ["//visibility:private"], +} + +// Extract the stub jar from "framework-all-test-api-host" for subsequent build rules. +java_genrule_host { + name: "framework-all-test-api-host-stub", + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-all-test-api-host{host_stub.jar}", + ], + out: [ + "host_stub.jar", + ], + visibility: ["//visibility:public"], +} + +// Extract the impl jar from "framework-all-test-api-host" for subsequent build rules. +java_genrule_host { + name: "framework-all-test-api-host-impl", + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-all-test-api-host{host_impl.jar}", + ], + out: [ + "host_impl.jar", + ], + visibility: ["//visibility:public"], +} + +// This library contains helper classes to build hostside tests/targets. +// This essentially contains dependencies from tests that we can't actually use the real ones. +// For example, the actual AndroidTestCase and AndroidJUnit4 don't run on the host side (yet), +// so we pup "fake" implementations here. +// Ideally this library should be empty. +java_library_host { + name: "hoststubgen-helper-framework-buildtime", + srcs: [ + "helper-framework-buildtime-src/**/*.java", + ], + libs: [ + // We need it to pull in some of the framework classes used in this library, + // such as Context.java. + "framework-all-hidden-api-host-impl", + "junit", + ], + visibility: ["//visibility:public"], +} + +// This module contains "fake" libcore/dalvik classes, framework native substitution, etc, +// that are needed at runtime. +java_library_host { + name: "hoststubgen-helper-framework-runtime", + srcs: [ + "helper-framework-runtime-src/**/*.java", + ], + libs: [ + "hoststubgen-helper-runtime", + "framework-all-hidden-api-host-impl", + ], + visibility: ["//visibility:public"], +} + +// Defaults for host side test modules. +// We need two rules for each test. +// 1. A "-test-lib" jar, which compiles the test against the stub jar. +// This one is only used by the second rule, so it should be "private. +// 2. A "-test" jar, which includes 1 + the runtime (impl) jars. + +// This and next ones are for tests using framework-app, with hidden APIs. +java_defaults { + name: "hosttest-with-framework-all-hidden-api-test-lib-defaults", + installable: false, + libs: [ + "framework-all-hidden-api-host-stub", + ], + static_libs: [ + "hoststubgen-helper-framework-buildtime", + "framework-annotations-lib", + ], + visibility: ["//visibility:private"], +} + +// Default rules to include `libandroid_runtime`. For now, it's empty, but we'll use it +// once we start using JNI. +java_defaults { + name: "hosttest-with-libandroid_runtime", + jni_libs: [ + // "libandroid_runtime", + + // TODO: Figure out how to build them automatically. + // Following ones are depended by libandroid_runtime. + // Without listing them here, not only we won't get them under + // $ANDROID_HOST_OUT/testcases/*/lib64, but also not under + // $ANDROID_HOST_OUT/lib64, so we'd fail to load them at runtime. + // ($ANDROID_HOST_OUT/lib/ gets all of them though.) + // "libcutils", + // "libharfbuzz_ng", + // "libminikin", + // "libz", + // "libbinder", + // "libhidlbase", + // "libvintf", + // "libicu", + // "libutils", + // "libtinyxml2", + ], +} + +java_defaults { + name: "hosttest-with-framework-all-hidden-api-test-defaults", + defaults: ["hosttest-with-libandroid_runtime"], + installable: false, + test_config: "AndroidTest-host.xml", + static_libs: [ + "hoststubgen-helper-runtime", + "hoststubgen-helper-framework-runtime", + "framework-all-hidden-api-host-impl", + ], +} + +// This and next ones are for tests using framework-app, with public/system/test APIs, +// without hidden APIs. +java_defaults { + name: "hosttest-with-framework-all-test-api-test-lib-defaults", + installable: false, + libs: [ + "framework-all-test-api-host-stub", + ], + static_libs: [ + "hoststubgen-helper-framework-buildtime", + "framework-annotations-lib", + ], + visibility: ["//visibility:private"], +} + +java_defaults { + name: "hosttest-with-framework-all-test-api-test-defaults", + defaults: ["hosttest-with-libandroid_runtime"], + installable: false, + test_config: "AndroidTest-host.xml", + static_libs: [ + "hoststubgen-helper-runtime", + "hoststubgen-helper-framework-runtime", + "framework-all-test-api-host-impl", + ], +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java new file mode 100644 index 000000000000..a774336a897c --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Add this with a fully-specified method name (e.g. {@code "com.package.Class.methodName"}) + * of a callback to get a callback at the class load time. + * + * The method must be {@code public static} with a single argument that takes + * {@link java.lang.Class}. + */ +@Target({TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestClassLoadHook { + String value(); +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java new file mode 100644 index 000000000000..06ad1c266a14 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Mark a class, field or a method as "Stub", meaning tests can _not_ see the APIs, but they + * can indirectly be used on the host side. + * When applied to a class, it will _not_ affect the visibility of its members. They need to be + * individually marked. + * + * <p>In order to expose a class and all its members, use {@link HostSideTestWholeClassStub} + * instead. + * @hide + */ +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestKeep { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java new file mode 100644 index 000000000000..9c8138351eb5 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * If a class has this annotation, all its native methods will be delegated to another class. + * (See {@link android.os.Parcel} as an example.) + */ +@Target({TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestNativeSubstitutionClass { + String value(); +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java new file mode 100644 index 000000000000..46e5078fb05d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Mark an item as "remove", so this cannot be used on the host side even indirectly. + * This is the default behavior. + * + * @hide + */ +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestRemove { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java new file mode 100644 index 000000000000..cabdfe0eeb77 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Mark a class, field or a method as "Stub", meaning tests can see the APIs. + * When applied to a class, it will _not_ affect the visibility of its members. They need to be + * individually marked. + * + * <p>In order to expose a class and all its members, use {@link HostSideTestWholeClassStub} + * instead. + * + * @hide + */ +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestStub { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java new file mode 100644 index 000000000000..510a67e0aaed --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * If a method has this annotation, we'll replace it with another method on the host side. + * + * See {@link android.util.LruCache#getEldest()} and its substitution. + * + * @hide + */ +@Target({METHOD}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestSubstitute { + // TODO We should add "_host" as default. We're not doing it yet, because extractign the default + // value with ASM doesn't seem trivial. (? not sure.) + String suffix(); +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java new file mode 100644 index 000000000000..cd1bef4be505 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * If a method has this annotation, it will throw on the host side. + * + * @hide + */ +@Target({METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestThrow { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java new file mode 100644 index 000000000000..3d1ddea2cbb7 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Same as {@link HostSideTestKeep} but it'll change the visibility of all its members too. + * @hide + */ +@Target({TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestWholeClassKeep { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java new file mode 100644 index 000000000000..1824f6f01516 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Same as {@link HostSideTestStub} but it'll change the visibility of all its members too. + * + * @hide + */ +@Target({TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestWholeClassStub { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java new file mode 100644 index 000000000000..b10f0ff1a4b1 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hosttest.annotation.tests; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Use this annotation to skip certain tests for host side tests. + * + * TODO: Actually use it in the test runner. + */ +@Target({TYPE, FIELD, METHOD}) +public @interface HostSideTestSuppress { +} diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/tools/hoststubgen/hoststubgen/framework-policy-override.txt new file mode 100644 index 000000000000..295498da02e4 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/framework-policy-override.txt @@ -0,0 +1,98 @@ +# -------------------------------------------------------------------------------------------------- +# This file contains rules to process `framework-all.jar` to generate the host side test "stub" and +# "impl" jars, without using Java annotations. +# +# Useful when: +# - The class is auto-generated and annotations can't be added. +# (We need to figure out what to do on auto-generated classes.) +# - Want to quickly change filter rules without having to rebuild framework.jar. +# +# Using this file, one can control the visibility of APIs on a per-class, per-field and per-method +# basis, but in most cases, per-class directives would be sufficient. That is: +# +# - To put the entire class, including its members and nested classes, in the "stub" jar, +# so that the test / target code can use the API, use `stubclass`. +# +# class package.class stubclass +# +# - To put the entire class, including its members and nested classes, in the "impl" jar, +# but not in the "stub" jar, use `keepclass`. Use this when you don't want to expose an API to +# tests/target directly, but it's still needed at runtime, because it's used by other "stub" APIs +# directly or indirectly. +# +# class package.class keepclass +# +# All other classes will be removed from both the stub jar and impl jar. +# +# -------------------------------------------------------------------------------------------------- + +# -------------------------------------------------------------------------------------------------- +# Directions on auto-generated classes, where we can't use Java annotations (yet). +# -------------------------------------------------------------------------------------------------- +class android.Manifest stubclass +class android.R stubclass +class android.os.PersistableBundleProto keepclass + +# This is in module-utils, where using a HostStubGen annotation would be complicated, so we +# add a direction here rather than using a java annotation. +# The build file says it's deprecated, anyway...? Figure out what to do with it. +class com.android.internal.util.Preconditions keepclass + +# -------------------------------------------------------------------------------------------------- +# Actual framework classes +# -------------------------------------------------------------------------------------------------- + +# Put basic exception classes in the "impl" jar. +# We don't put them in stub yet (until something actually needs them). +class android.os.DeadObjectException keepclass +class android.os.DeadSystemRuntimeException keepclass +class android.os.NetworkOnMainThreadException keepclass +class android.os.RemoteException keepclass +class android.os.ServiceSpecificException keepclass +class android.util.AndroidException keepclass +class android.util.AndroidRuntimeException keepclass +class android.os.DeadSystemException keepclass + + +# For now, we only want to expose ArrayMap and Log, but they and their tests depend on +# more classes. + +class android.util.ArrayMap stubclass + +# Used by ArrayMap. No need to put them in the stub, but we need them in impl. +class android.util.MapCollections keepclass +class android.util.ContainerHelpers keepclass +class com.android.internal.util.XmlUtils keepclass +class com.android.internal.util.FastMath keepclass +class android.util.MathUtils keepclass + + +class android.util.Log stubclass +class android.util.Slog stubclass +# We don't use Log's native code, yet. Instead, the following line enables the Java substitution. +# Comment it out to disable Java substitution of Log's native methods. +class android.util.Log !com.android.hoststubgen.nativesubstitution.Log_host + +# Used by log +class com.android.internal.util.FastPrintWriter keepclass +class com.android.internal.util.LineBreakBufferedWriter keepclass + + +# Expose Context because it's referred to by AndroidTestCase, but don't need to expose any of +# its members. +class android.content.Context keep + +# Expose Parcel, Parcel and there relevant classes, which are used by ArrayMapTets. +class android.os.Parcelable StubClass +class android.os.Parcel StubClass +class android.os.Parcel !com.android.hoststubgen.nativesubstitution.Parcel_host + +class android.os.IBinder stubClass +class android.os.IInterface stubclass + +class android.os.BadParcelableException stubclass +class android.os.BadTypeParcelableException stubclass + +class android.os.BaseBundle stubclass +class android.os.Bundle stubclass +class android.os.PersistableBundle stubclass diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java new file mode 100644 index 000000000000..e6d38668335a --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.test; + +import android.content.Context; + +import junit.framework.TestCase; + +public class AndroidTestCase extends TestCase { + protected Context mContext; + public Context getContext() { + throw new RuntimeException("[ravenwood] Class Context is not supported yet."); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java new file mode 100644 index 000000000000..51c5d9a05e52 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.annotation; + +// [ravenwood] TODO: Find the actual androidx jar containing it.s + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Denotes that a parameter, field or method return value can never be null. + * <p> + * This is a marker annotation and it has no specific attributes. + * + * @paramDoc This value cannot be {@code null}. + * @returnDoc This value cannot be {@code null}. + * @hide + */ +@Retention(SOURCE) +@Target({METHOD, PARAMETER, FIELD}) +public @interface NonNull { +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java new file mode 100644 index 000000000000..f1f0e8b43f16 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.annotation; + +// [ravenwood] TODO: Find the actual androidx jar containing it.s + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Denotes that a parameter, field or method return value can be null. + * <p> + * When decorating a method call parameter, this denotes that the parameter can + * legitimately be null and the method will gracefully deal with it. Typically + * used on optional parameters. + * <p> + * When decorating a method, this denotes the method might legitimately return + * null. + * <p> + * This is a marker annotation and it has no specific attributes. + * + * @paramDoc This value may be {@code null}. + * @returnDoc This value may be {@code null}. + * @hide + */ +@Retention(SOURCE) +@Target({METHOD, PARAMETER, FIELD}) +public @interface Nullable { +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java new file mode 100644 index 000000000000..0c82e4e268d3 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.test.ext.junit.runners; + +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.InitializationError; + +// TODO: We need to simulate the androidx test runner. +// https://source.corp.google.com/piper///depot/google3/third_party/android/androidx_test/ext/junit/java/androidx/test/ext/junit/runners/AndroidJUnit4.java +// https://source.corp.google.com/piper///depot/google3/third_party/android/androidx_test/runner/android_junit_runner/java/androidx/test/internal/runner/junit4/AndroidJUnit4ClassRunner.java + +public class AndroidJUnit4 extends BlockJUnit4ClassRunner { + public AndroidJUnit4(Class<?> testClass) throws InitializationError { + super(testClass); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java new file mode 100644 index 000000000000..2470d8390f5d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Designates a test as being flaky (non-deterministic). + * + * <p>Can then be used to filter tests on execution using -e annotation or -e notAnnotation as + * desired. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface FlakyTest { + /** + * An optional bug number associated with the test. -1 Means that no bug number is associated with + * the flaky annotation. + * + * @return int + */ + int bugId() default -1; + + /** + * Details, such as the reason of why the test is flaky. + * + * @return String + */ + String detail() default ""; +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java new file mode 100644 index 000000000000..578d7dc73647 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to assign a large test size qualifier to a test. This annotation can be used at a + * method or class level. + * + * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a + * test suite of similar run time. + * + * <p>Execution time: >1000ms + * + * <p>Large tests should be focused on testing integration of all application components. These + * tests fully participate in the system and may make use of all resources such as databases, file + * systems and network. As a rule of thumb most functional UI tests are large tests. + * + * <p>Note: This class replaces the deprecated Android platform size qualifier <a + * href="{@docRoot}reference/android/test/suitebuilder/annotation/LargeTest.html"><code> + * android.test.suitebuilder.annotation.LargeTest</code></a> and is the recommended way to annotate + * tests written with the AndroidX Test Library. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface LargeTest {} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java new file mode 100644 index 000000000000..dfdaa53ee6ac --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to assign a medium test size qualifier to a test. This annotation can be used at a + * method or class level. + * + * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a + * test suite of similar run time. + * + * <p>Execution time: <1000ms + * + * <p>Medium tests should be focused on a very limited subset of components or a single component. + * Resource access to the file system through well defined interfaces like databases, + * ContentProviders, or Context is permitted. Network access should be restricted, (long-running) + * blocking operations should be avoided and use mock objects instead. + * + * <p>Note: This class replaces the deprecated Android platform size qualifier <a + * href="{@docRoot}reference/android/test/suitebuilder/annotation/MediumTest.html"><code> + * android.test.suitebuilder.annotation.MediumTest</code></a> and is the recommended way to annotate + * tests written with the AndroidX Test Library. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface MediumTest {} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java new file mode 100644 index 000000000000..3d3ee3318bfa --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that a specific test should not be run on emulator. + * + * <p>It will be executed only if the test is running on the physical android device. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface RequiresDevice {} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java new file mode 100644 index 000000000000..dd65ddb382dc --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that a specific test or class requires a minimum or maximum API Level to execute. + * + * <p>Test(s) will be skipped when executed on android platforms less/more than specified level + * (inclusive). + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface SdkSuppress { + /** The minimum API level to execute (inclusive) */ + int minSdkVersion() default 1; + /** The maximum API level to execute (inclusive) */ + int maxSdkVersion() default Integer.MAX_VALUE; + /** + * The {@link android.os.Build.VERSION.CODENAME} to execute on. This is intended to be used to run + * on a pre-release SDK, where the {@link android.os.Build.VERSION.SDK_INT} has not yet been + * finalized. This is treated as an OR operation with respect to the minSdkVersion and + * maxSdkVersion attributes. + * + * <p>For example, to filter a test so it runs on only the prerelease R SDK: <code> + * {@literal @}SdkSuppress(minSdkVersion = Build.VERSION_CODES.R, codeName = "R") + * </code> + */ + String codeName() default "unset"; +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java new file mode 100644 index 000000000000..dd32df44effe --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to assign a small test size qualifier to a test. This annotation can be used at a + * method or class level. + * + * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a + * test suite of similar run time. + * + * <p>Execution time: <200ms + * + * <p>Small tests should be run very frequently. Focused on units of code to verify specific logical + * conditions. These tests should runs in an isolated environment and use mock objects for external + * dependencies. Resource access (such as file system, network, or databases) are not permitted. + * Tests that interact with hardware, make binder calls, or that facilitate android instrumentation + * should not use this annotation. + * + * <p>Note: This class replaces the deprecated Android platform size qualifier <a + * href="http://developer.android.com/reference/android/test/suitebuilder/annotation/SmallTest.html"> + * android.test.suitebuilder.annotation.SmallTest</a> and is the recommended way to annotate tests + * written with the AndroidX Test Library. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface SmallTest {} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java new file mode 100644 index 000000000000..88e636c2dd77 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Use this annotation on test classes or test methods that should not be included in a test suite. + * If the annotation appears on the class then no tests in that class will be included. If the + * annotation appears only on a test method then only that method will be excluded. + * + * <p>Note: This class replaces the deprecated Android platform annotation <a + * href="http://developer.android.com/reference/android/test/suitebuilder/annotation/Suppress.html"> + * android.test.suitebuilder.annotation.Suppress</a> and is the recommended way to suppress tests + * written with the AndroidX Test Library. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface Suppress {} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java new file mode 100644 index 000000000000..e1379390f98b --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.test.runner; + +import org.junit.runners.model.InitializationError; + +public class AndroidJUnit4 extends androidx.test.ext.junit.runners.AndroidJUnit4 { + public AndroidJUnit4(Class<?> testClass) throws InitializationError { + super(testClass); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java new file mode 100644 index 000000000000..ee55c7ac6ae2 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.nativesubstitution; + +import android.util.Log; +import android.util.Log.Level; + +import java.io.PrintStream; + +public class Log_host { + + public static boolean isLoggable(String tag, @Level int level) { + return true; + } + + public static int println_native(int bufID, int priority, String tag, String msg) { + final PrintStream out = System.out; + final String buffer; + switch (bufID) { + case Log.LOG_ID_MAIN: buffer = "main"; break; + case Log.LOG_ID_RADIO: buffer = "radio"; break; + case Log.LOG_ID_EVENTS: buffer = "event"; break; + case Log.LOG_ID_SYSTEM: buffer = "system"; break; + case Log.LOG_ID_CRASH: buffer = "crash"; break; + default: buffer = "buf:" + bufID; break; + }; + + final String prio; + switch (priority) { + case Log.VERBOSE: prio = "V"; break; + case Log.DEBUG: prio = "D"; break; + case Log.INFO: prio = "I"; break; + case Log.WARN: prio = "W"; break; + case Log.ERROR: prio = "E"; break; + case Log.ASSERT: prio = "A"; break; + default: prio = "prio:" + priority; break; + }; + + for (String s : msg.split("\\n")) { + out.println(String.format("logd: [%s] %s %s: %s", buffer, prio, tag, s)); + } + return msg.length(); + } + + public static int logger_entry_max_payload_native() { + return 4068; // [ravenwood] This is what people use in various places. + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java new file mode 100644 index 000000000000..d749f076f3b5 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.nativesubstitution; + +import android.os.IBinder; + +import java.io.FileDescriptor; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Tentative, partial implementation of the Parcel native methods, using Java's + * {@link ByteBuffer}. It turned out there's enough semantics differences between Parcel + * and {@link ByteBuffer}, so it didn't actually work. + * (e.g. Parcel seems to allow moving the data position to be beyond its size? Which + * {@link ByteBuffer} wouldn't allow...) + */ +public class Parcel_host { + private Parcel_host() { + } + + private static final AtomicLong sNextId = new AtomicLong(0); + + private static final Map<Long, Parcel_host> sInstances = new ConcurrentHashMap<>(); + + private boolean mDeleted = false; + + private byte[] mBuffer; + private int mSize; + private int mPos; + + private boolean mSensitive; + private boolean mAllowFds; + + // TODO Use the actual value from Parcel.java. + private static final int OK = 0; + + private void validate() { + if (mDeleted) { + // TODO: Put more info + throw new RuntimeException("Parcel already destroyed"); + } + } + + private static Parcel_host getInstance(long id) { + Parcel_host p = sInstances.get(id); + if (p == null) { + // TODO: Put more info + throw new RuntimeException("Parcel doesn't exist with id=" + id); + } + p.validate(); + return p; + } + + public static long nativeCreate() { + final long id = sNextId.getAndIncrement(); + final Parcel_host p = new Parcel_host(); + sInstances.put(id, p); + p.init(); + return id; + } + + private void init() { + mBuffer = new byte[0]; + mSize = 0; + mPos = 0; + mSensitive = false; + mAllowFds = false; + } + + private void updateSize() { + if (mSize < mPos) { + mSize = mPos; + } + } + + public static void nativeDestroy(long nativePtr) { + getInstance(nativePtr).mDeleted = true; + sInstances.remove(nativePtr); + } + + public static void nativeFreeBuffer(long nativePtr) { + getInstance(nativePtr).freeBuffer(); + } + + public void freeBuffer() { + init(); + } + + private int getCapacity() { + return mBuffer.length; + } + + private void ensureMoreCapacity(int size) { + ensureCapacity(mPos + size); + } + + private void ensureCapacity(int targetSize) { + if (targetSize <= getCapacity()) { + return; + } + var newSize = getCapacity() * 2; + if (newSize < targetSize) { + newSize = targetSize; + } + forceSetCapacity(newSize); + } + + private void forceSetCapacity(int newSize) { + var newBuf = new byte[newSize]; + + // Copy + System.arraycopy(mBuffer, 0, newBuf, 0, Math.min(newSize, getCapacity())); + + this.mBuffer = newBuf; + } + + private void ensureDataAvailable(int requestSize) { + if (mSize - mPos < requestSize) { + throw new RuntimeException(String.format( + "Pacel data underflow. size=%d, pos=%d, request=%d", mSize, mPos, requestSize)); + } + } + + public static void nativeMarkSensitive(long nativePtr) { + getInstance(nativePtr).mSensitive = true; + } + public static void nativeMarkForBinder(long nativePtr, IBinder binder) { + throw new RuntimeException("Not implemented yet"); + } + public static boolean nativeIsForRpc(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + public static int nativeDataSize(long nativePtr) { + return getInstance(nativePtr).mSize; + } + public static int nativeDataAvail(long nativePtr) { + var p = getInstance(nativePtr); + return p.mSize - p.mPos; + } + public static int nativeDataPosition(long nativePtr) { + return getInstance(nativePtr).mPos; + } + public static int nativeDataCapacity(long nativePtr) { + return getInstance(nativePtr).mBuffer.length; + } + public static void nativeSetDataSize(long nativePtr, int size) { + var p = getInstance(nativePtr); + p.ensureCapacity(size); + getInstance(nativePtr).mSize = size; + } + public static void nativeSetDataPosition(long nativePtr, int pos) { + var p = getInstance(nativePtr); + // TODO: Should this change the size or the capacity?? + p.mPos = pos; + } + public static void nativeSetDataCapacity(long nativePtr, int size) { + var p = getInstance(nativePtr); + if (p.getCapacity() < size) { + p.forceSetCapacity(size); + } + } + + public static boolean nativePushAllowFds(long nativePtr, boolean allowFds) { + var p = getInstance(nativePtr); + var prev = p.mAllowFds; + p.mAllowFds = allowFds; + return prev; + } + public static void nativeRestoreAllowFds(long nativePtr, boolean lastValue) { + getInstance(nativePtr).mAllowFds = lastValue; + } + + public static void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len) { + nativeWriteBlob(nativePtr, b, offset, len); + } + + public static void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len) { + var p = getInstance(nativePtr); + + if (b == null) { + nativeWriteInt(nativePtr, -1); + } else { + final var alignedSize = align4(b.length); + + nativeWriteInt(nativePtr, b.length); + + p.ensureMoreCapacity(alignedSize); + + System.arraycopy(b, offset, p.mBuffer, p.mPos, len); + p.mPos += alignedSize; + p.updateSize(); + } + } + + public static int nativeWriteInt(long nativePtr, int value) { + var p = getInstance(nativePtr); + p.ensureMoreCapacity(Integer.BYTES); + + p.mBuffer[p.mPos++] = (byte) ((value >> 24) & 0xff); + p.mBuffer[p.mPos++] = (byte) ((value >> 16) & 0xff); + p.mBuffer[p.mPos++] = (byte) ((value >> 8) & 0xff); + p.mBuffer[p.mPos++] = (byte) ((value >> 0) & 0xff); + + p.updateSize(); + + return OK; + } + + public static int nativeWriteLong(long nativePtr, long value) { + nativeWriteInt(nativePtr, (int) (value >>> 32)); + nativeWriteInt(nativePtr, (int) (value)); + return OK; + } + public static int nativeWriteFloat(long nativePtr, float val) { + return nativeWriteInt(nativePtr, Float.floatToIntBits(val)); + } + public static int nativeWriteDouble(long nativePtr, double val) { + return nativeWriteLong(nativePtr, Double.doubleToLongBits(val)); + } + public static void nativeSignalExceptionForError(int error) { + throw new RuntimeException("Not implemented yet"); + } + + private static int align4(int val) { + return ((val + 3) / 4) * 4; + } + + public static void nativeWriteString8(long nativePtr, String val) { + if (val == null) { + nativeWriteBlob(nativePtr, null, 0, 0); + } else { + var bytes = val.getBytes(StandardCharsets.UTF_8); + nativeWriteBlob(nativePtr, bytes, 0, bytes.length); + } + } + public static void nativeWriteString16(long nativePtr, String val) { + // Just reuse String8 + nativeWriteString8(nativePtr, val); + } + public static void nativeWriteStrongBinder(long nativePtr, IBinder val) { + throw new RuntimeException("Not implemented yet"); + } + public static void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val) { + throw new RuntimeException("Not implemented yet"); + } + + public static byte[] nativeCreateByteArray(long nativePtr) { + return nativeReadBlob(nativePtr); + } + + public static boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen) { + if (dest == null) { + return false; + } + var data = nativeReadBlob(nativePtr); + if (data == null) { + System.err.println("Percel has NULL, which is unexpected."); // TODO: Is this correct? + return false; + } + // TODO: Make sure the check logic is correct. + if (data.length != destLen) { + System.err.println("Byte array size mismatch: expected=" + + data.length + " given=" + destLen); + return false; + } + return true; + } + + public static byte[] nativeReadBlob(long nativePtr) { + final var size = nativeReadInt(nativePtr); + if (size == -1) { + return null; + } + var p = getInstance(nativePtr); + p.ensureDataAvailable(size); + + var bytes = new byte[size]; + System.arraycopy(p.mBuffer, p.mPos, bytes, 0, size); + + p.mPos += align4(size); + + return bytes; + } + public static int nativeReadInt(long nativePtr) { + var p = getInstance(nativePtr); + + p.ensureDataAvailable(Integer.BYTES); + + var ret = (((p.mBuffer[p.mPos++] & 0xff) << 24) + | ((p.mBuffer[p.mPos++] & 0xff) << 16) + | ((p.mBuffer[p.mPos++] & 0xff) << 8) + | ((p.mBuffer[p.mPos++] & 0xff) << 0)); + + return ret; + } + public static long nativeReadLong(long nativePtr) { + return (((long) nativeReadInt(nativePtr)) << 32) + | (((long) nativeReadInt(nativePtr)) & 0xffff_ffffL); + } + + public static float nativeReadFloat(long nativePtr) { + return Float.intBitsToFloat(nativeReadInt(nativePtr)); + } + + public static double nativeReadDouble(long nativePtr) { + return Double.longBitsToDouble(nativeReadLong(nativePtr)); + } + + public static String nativeReadString8(long nativePtr) { + final var bytes = nativeReadBlob(nativePtr); + if (bytes == null) { + return null; + } + return new String(bytes, StandardCharsets.UTF_8); + } + public static String nativeReadString16(long nativePtr) { + return nativeReadString8(nativePtr); + } + public static IBinder nativeReadStrongBinder(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + public static FileDescriptor nativeReadFileDescriptor(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + + public static byte[] nativeMarshall(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + public static void nativeUnmarshall( + long nativePtr, byte[] data, int offset, int length) { + throw new RuntimeException("Not implemented yet"); + } + public static int nativeCompareData(long thisNativePtr, long otherNativePtr) { + throw new RuntimeException("Not implemented yet"); + } + public static boolean nativeCompareDataInRange( + long ptrA, int offsetA, long ptrB, int offsetB, int length) { + throw new RuntimeException("Not implemented yet"); + } + public static void nativeAppendFrom( + long thisNativePtr, long otherNativePtr, int srcOffset, int length) { + var dst = getInstance(thisNativePtr); + var src = getInstance(otherNativePtr); + + dst.ensureMoreCapacity(length); + + System.arraycopy(src.mBuffer, srcOffset, dst.mBuffer, dst.mPos, length); + dst.mPos += length; // TODO: 4 byte align? + dst.updateSize(); + + // TODO: Update the other's position? + } + + public static boolean nativeHasFileDescriptors(long nativePtr) { + // Assume false for now, because we don't support writing FDs yet. + return false; + } + public static boolean nativeHasFileDescriptorsInRange( + long nativePtr, int offset, int length) { + // Assume false for now, because we don't support writing FDs yet. + return false; + } + public static void nativeWriteInterfaceToken(long nativePtr, String interfaceName) { + throw new RuntimeException("Not implemented yet"); + } + public static void nativeEnforceInterface(long nativePtr, String interfaceName) { + throw new RuntimeException("Not implemented yet"); + } + + public static boolean nativeReplaceCallingWorkSourceUid( + long nativePtr, int workSourceUid) { + throw new RuntimeException("Not implemented yet"); + } + public static int nativeReadCallingWorkSourceUid(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + + public static long nativeGetOpenAshmemSize(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + public static long getGlobalAllocSize() { + throw new RuntimeException("Not implemented yet"); + } + public static long getGlobalAllocCount() { + throw new RuntimeException("Not implemented yet"); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java new file mode 100644 index 000000000000..1ec1d5f307e1 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.nativesubstitution; + +public class SystemProperties_host { + public static String native_get(String key, String def) { + throw new RuntimeException("Not implemented yet"); + } + public static int native_get_int(String key, int def) { + throw new RuntimeException("Not implemented yet"); + } + public static long native_get_long(String key, long def) { + throw new RuntimeException("Not implemented yet"); + } + public static boolean native_get_boolean(String key, boolean def) { + throw new RuntimeException("Not implemented yet"); + } + + public static long native_find(String name) { + throw new RuntimeException("Not implemented yet"); + } + public static String native_get(long handle) { + throw new RuntimeException("Not implemented yet"); + } + public static int native_get_int(long handle, int def) { + throw new RuntimeException("Not implemented yet"); + } + public static long native_get_long(long handle, long def) { + throw new RuntimeException("Not implemented yet"); + } + public static boolean native_get_boolean(long handle, boolean def) { + throw new RuntimeException("Not implemented yet"); + } + public static void native_set(String key, String def) { + throw new RuntimeException("Not implemented yet"); + } + public static void native_add_change_callback() { + throw new RuntimeException("Not implemented yet"); + } + public static void native_report_sysprop_change() { + throw new RuntimeException("Not implemented yet"); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java new file mode 100644 index 000000000000..fbcc64892798 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.runtimehelper; + +import com.android.hoststubgen.hosthelper.HostTestException; + +import java.io.File; +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.util.ArrayList; + +/** + * Standard class loader hook. + * + * Currently, we use this class to load libandroid_runtime (if needed). In the future, we may + * load other JNI or do other set up here. + */ +public class ClassLoadHook { + private static PrintStream sOut = System.out; + + /** + * If true, we won't load `libandroid_runtime` + * + * <p>Looks like there's some complexity in running a host test with JNI with `atest`, + * so we need a way to remove the dependency. + */ + private static final boolean SKIP_LOADING_LIBANDROID = "1".equals(System.getenv( + "HOSTTEST_SKIP_LOADING_LIBANDROID")); + + public static final String CORE_NATIVE_CLASSES = "core_native_classes"; + public static final String ICU_DATA_PATH = "icu.data.path"; + public static final String KEYBOARD_PATHS = "keyboard_paths"; + public static final String GRAPHICS_NATIVE_CLASSES = "graphics_native_classes"; + + public static final String VALUE_N_A = "**n/a**"; + public static final String LIBANDROID_RUNTIME_NAME = "libandroid_runtime"; + + private static String sInitialDir = new File("").getAbsolutePath(); + + static { + log("Initialized. Current dir=" + sInitialDir); + } + + private ClassLoadHook() { + } + + /** + * Called when classes with + * {@code @HostSideTestClassLoadHook( + * "com.android.hoststubgen.runtimehelper.LibandroidLoadingHook.onClassLoaded") } + * are loaded. + */ + public static void onClassLoaded(Class<?> clazz) { + System.out.println("Framework class loaded: " + clazz.getCanonicalName()); + + loadFrameworkNativeCode(); + } + + private static void log(String message) { + sOut.println("ClassLoadHook: " + message); + } + + private static void log(String fmt, Object... args) { + log(String.format(fmt, args)); + } + + private static void ensurePropertyNotSet(String key) { + if (System.getProperty(key) != null) { + throw new HostTestException("System property \"" + key + "\" is set unexpectedly"); + } + } + + private static void setProperty(String key, String value) { + System.setProperty(key, value); + log("Property set: %s=\"%s\"", key, value); + } + + private static void dumpSystemProperties() { + for (var prop : System.getProperties().entrySet()) { + log(" %s=\"%s\"", prop.getKey(), prop.getValue()); + } + } + + private static void loadJniLibrary(String name) { + final String path = sInitialDir + "/lib64/" + name + ".so"; + System.out.println("Loading " + path + " ..."); + System.load(path); + System.out.println("Done loading " + path); + } + + private static boolean sLoadFrameworkNativeCodeCalled = false; + + /** + * Load `libandroid_runtime` if needed. + */ + private static void loadFrameworkNativeCode() { + // This is called from class-initializers, so no synchronization is needed. + if (sLoadFrameworkNativeCodeCalled) { + // This method has already been called before.s + return; + } + sLoadFrameworkNativeCodeCalled = true; + + // libandroid_runtime uses Java's system properties to decide what JNI methods to set up. + // Set up these properties for host-side tests. + + if ("1".equals(System.getenv("HOSTTEST_DUMP_PROPERTIES"))) { + log("Java system properties:"); + dumpSystemProperties(); + } + + if (SKIP_LOADING_LIBANDROID) { + log("Skip loading " + LIBANDROID_RUNTIME_NAME); + } + + // Make sure these properties are not set. + ensurePropertyNotSet(CORE_NATIVE_CLASSES); + ensurePropertyNotSet(ICU_DATA_PATH); + ensurePropertyNotSet(KEYBOARD_PATHS); + ensurePropertyNotSet(GRAPHICS_NATIVE_CLASSES); + + // Tell libandroid what JNI to use. + final var jniClasses = getCoreNativeClassesToUse(); + if (jniClasses.isEmpty()) { + log("No classes require JNI methods, skip loading " + LIBANDROID_RUNTIME_NAME); + return; + } + setProperty(CORE_NATIVE_CLASSES, jniClasses); + setProperty(GRAPHICS_NATIVE_CLASSES, ""); + setProperty(ICU_DATA_PATH, VALUE_N_A); + setProperty(KEYBOARD_PATHS, VALUE_N_A); + + loadJniLibrary(LIBANDROID_RUNTIME_NAME); + } + + /** + * Classes with native methods that are backed by `libandroid_runtime`. + * + * At runtime, we check if these classes have any methods, and if so, we'll have + * `libandroid_runtime` register the native functions. + */ + private static final Class<?>[] sClassesWithLibandroidNativeMethods = { + android.util.Log.class, + android.os.Parcel.class, + }; + + /** + * @return if a given method is a native method or not. + */ + private static boolean hasNativeMethod(Class<?> clazz) { + for (var method : clazz.getDeclaredMethods()) { + if (Modifier.isNative(method.getModifiers())) { + return true; + } + } + return false; + } + + /** + * Create a list of classes as comma-separated that require JNI methods to be set up. + * + * <p>This list is used by frameworks/base/core/jni/LayoutlibLoader.cpp to decide + * what JNI methods to set up. + */ + private static String getCoreNativeClassesToUse() { + final var coreNativeClassesToLoad = new ArrayList<String>(); + + for (var clazz : sClassesWithLibandroidNativeMethods) { + if (hasNativeMethod(clazz)) { + log("Class %s has native methods", clazz); + coreNativeClassesToLoad.add(clazz.getName()); + } + } + + return String.join(",", coreNativeClassesToLoad); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java new file mode 100644 index 000000000000..7d2b00d9420d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dalvik.system; + +// [ravenwood] It's in libart, so until we get ART to work, we need to use a fake. +// The original is here: +// $ANDROID_BUILD_TOP/libcore/libart/src/main/java/dalvik/system/VMRuntime.java + +import java.lang.reflect.Array; + +public class VMRuntime { + private static final VMRuntime THE_ONE = new VMRuntime(); + + private VMRuntime() { + } + + public static VMRuntime getRuntime() { + return THE_ONE; + } + + public boolean is64Bit() { + return true; + } + + public static boolean is64BitAbi(String abi) { + return true; + } + + public Object newUnpaddedArray(Class<?> componentType, int minLength) { + return Array.newInstance(componentType, minLength); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java new file mode 100644 index 000000000000..a1ae35a88656 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package libcore.util; + +import java.lang.annotation.Annotation; + +// [ravenwood] Copied from libcore. TODO: Figure out what to do with libcore. +public class EmptyArray { + private EmptyArray() {} + + public static final boolean[] BOOLEAN = new boolean[0]; + + public static final byte[] BYTE = new byte[0]; + + public static final char[] CHAR = new char[0]; + + public static final double[] DOUBLE = new double[0]; + + public static final float[] FLOAT = new float[0]; + + public static final int[] INT = new int[0]; + + public static final long[] LONG = new long[0]; + + public static final Class<?>[] CLASS = new Class[0]; + + public static final Object[] OBJECT = new Object[0]; + + public static final String[] STRING = new String[0]; + + public static final Throwable[] THROWABLE = new Throwable[0]; + + public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0]; + + public static final java.lang.reflect.Type[] TYPE = new java.lang.reflect.Type[0]; + + public static final java.lang.reflect.TypeVariable[] TYPE_VARIABLE = + new java.lang.reflect.TypeVariable[0]; + public static final Annotation[] ANNOTATION = new Annotation[0]; + +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java new file mode 100644 index 000000000000..e142c46bc311 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.util; + +// [ravenwood] Copied from libcore. TODO: Figure out what to do with libcore. + +public class SneakyThrow { + + private SneakyThrow() { + } + + public static void sneakyThrow(Throwable t) { + SneakyThrow.<RuntimeException>sneakyThrow_(t); + } + + private static <T extends Throwable> void sneakyThrow_(Throwable t) throws T { + throw (T) t; + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java new file mode 100644 index 000000000000..4c37579ac917 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.hosthelper; + +import static java.lang.annotation.ElementType.TYPE; + +import org.objectweb.asm.Type; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation added to all "stub" classes generated by HostStubGen. + */ +@Target({TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface HostStubGenProcessedKeepClass { + String CLASS_INTERNAL_NAME = Type.getInternalName(HostStubGenProcessedKeepClass.class); + String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";"; +} diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java new file mode 100644 index 000000000000..34e0030f548a --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.hosthelper; + +import static java.lang.annotation.ElementType.TYPE; + +import org.objectweb.asm.Type; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation added to all "stub" classes generated by HostStubGen. + */ +@Target({TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface HostStubGenProcessedStubClass { + String CLASS_INTERNAL_NAME = Type.getInternalName(HostStubGenProcessedStubClass.class); + String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";"; +} diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java new file mode 100644 index 000000000000..c54c2c111229 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.hosthelper; + +public class HostTestException extends RuntimeException { + public HostTestException(String message) { + super(message); + } + + public HostTestException(String message, Throwable inner) { + super(message, inner); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java new file mode 100644 index 000000000000..29f7be008eef --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.hosthelper; + +import com.google.common.reflect.ClassPath; +import com.google.common.reflect.ClassPath.ClassInfo; + +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestSuite; + +import java.util.regex.Pattern; + +/** + * A very simple Junit {@link TestSuite} builder that finds all classes that look like test classes. + * + * We use it to run ravenwood test jars from the command line. + */ +public class HostTestSuite { + private static final String CLASS_NAME_REGEX_ENV = "HOST_TEST_CLASS_NAME_REGEX"; + + /** + * Called by junit, and return all test-looking classes as a suite. + */ + public static TestSuite suite() { + TestSuite suite = new TestSuite(); + + final Pattern classNamePattern; + final var filterRegex = System.getenv(CLASS_NAME_REGEX_ENV); + if (filterRegex == null) { + classNamePattern = Pattern.compile(""); + } else { + classNamePattern = Pattern.compile(filterRegex); + } + try { + // We use guava to list all classes. + ClassPath cp = ClassPath.from(HostTestSuite.class.getClassLoader()); + + for (var classInfo : cp.getAllClasses()) { + Class<?> clazz = asTestClass(classInfo); + if (clazz != null) { + if (classNamePattern.matcher(clazz.getSimpleName()).find()) { + System.out.println("Test class found " + clazz.getName()); + } else { + System.out.println("Skipping test class (for $" + + CLASS_NAME_REGEX_ENV + "): " + clazz.getName()); + } + suite.addTest(new JUnit4TestAdapter(clazz)); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return suite; + } + + /** + * Decide whether a class looks like a test class or not, and if so, return it as a Class + * instance. + */ + private static Class<?> asTestClass(ClassInfo classInfo) { + try { + final Class<?> clazz = classInfo.load(); + + // Does it extend junit.framework.TestCase? + if (junit.framework.TestCase.class.isAssignableFrom(clazz)) { + // Ignore classes in JUnit itself, or the android(x) test lib. + if (classInfo.getName().startsWith("junit.") + || classInfo.getName().startsWith("org.junit.") + || classInfo.getName().startsWith("android.test.") + || classInfo.getName().startsWith("androidx.test.")) { + return null; // Ignore junit classes. + } + return clazz; + } + // Does it have any "@Test" method? + for (var method : clazz.getMethods()) { + for (var an : method.getAnnotations()) { + if (an.annotationType() == org.junit.Test.class) { + return clazz; + } + } + } + return null; + } catch (java.lang.NoClassDefFoundError e) { + // Ignore unloadable classes. + return null; + } + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java new file mode 100644 index 000000000000..f7719a6e55b2 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.hosthelper; + +import org.objectweb.asm.Type; + +import java.io.PrintStream; +import java.lang.StackWalker.Option; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; + +import javax.annotation.concurrent.GuardedBy; + +/** + * Utilities used in the host side test environment. + */ +public class HostTestUtils { + private HostTestUtils() { + } + + public static final String CLASS_INTERNAL_NAME = Type.getInternalName(HostTestUtils.class); + + /** If true, we won't print method call log. */ + private static final boolean SKIP_METHOD_LOG = "1".equals(System.getenv( + "HOSTTEST_SKIP_METHOD_LOG")); + + /** If true, we won't perform non-stub method direct call check. */ + private static final boolean SKIP_NON_STUB_METHOD_CHECK = "1".equals(System.getenv( + "HOSTTEST_SKIP_NON_STUB_METHOD_CHECK")); + + + /** + * Method call log will be printed to it. + */ + public static PrintStream logPrintStream = System.out; + + /** + * Called from methods with FilterPolicy.Throw. + */ + public static void onThrowMethodCalled() { + // TODO: Maybe add call tracking? + throw new RuntimeException("This method is not supported on the host side"); + } + + /** + * Called from methods with FilterPolicy.Log. + */ + public static void logMethodCall( + String methodClass, + String methodName, + String methodDescriptor + ) { + if (SKIP_METHOD_LOG) { + return; + } + logPrintStream.println("# " + methodClass + "." + methodName + methodDescriptor); + } + + private static final StackWalker sStackWalker = + StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE); + + /** + * Return a {@link StackWalker} that supports {@link StackWalker#getCallerClass()}. + */ + public static StackWalker getStackWalker() { + return sStackWalker; + } + + /** + * Cache used by {@link #isClassAllowedToCallNonStubMethods}. + */ + @GuardedBy("sAllowedClasses") + private static final HashMap<Class, Boolean> sAllowedClasses = new HashMap(); + + /** + * Return true if a given class is allowed to access non-stub methods -- that is, if the class + * is in the hoststubgen generated JARs. (not in the test jar.) + */ + private static boolean isClassAllowedToCallNonStubMethods(Class<?> clazz) { + synchronized (sAllowedClasses) { + var cached = sAllowedClasses.get(clazz); + if (cached != null) { + return cached; + } + } + // All processed classes have this annotation. + var allowed = clazz.getAnnotation(HostStubGenProcessedKeepClass.class) != null; + + // Java classes should be able to access any methods. (via callbacks, etc.) + if (!allowed) { + if (clazz.getPackageName().startsWith("java.") + || clazz.getPackageName().startsWith("javax.")) { + allowed = true; + } + } + synchronized (sAllowedClasses) { + sAllowedClasses.put(clazz, allowed); + } + return allowed; + } + + /** + * Called when non-stub methods are called. We do a host-unsupported method direct call check + * in here. + */ + public static void onNonStubMethodCalled( + String methodClass, + String methodName, + String methodDescriptor, + Class<?> callerClass) { + if (SKIP_NON_STUB_METHOD_CHECK) { + return; + } + if (isClassAllowedToCallNonStubMethods(callerClass)) { + return; // Generated class is allowed to call framework class. + } + logPrintStream.println("! " + methodClass + "." + methodName + methodDescriptor + + " called by " + callerClass.getCanonicalName()); + } + + /** + * Called when any top level class (not nested classes) in the impl jar is loaded. + * + * When HostStubGen inject a class-load hook, it's always a call to this method, with the + * actual method name as the second argument. + * + * This method discovers the hook method with reflections and call it. + * + * TODO: Add a unit test. + */ + public static void onClassLoaded(Class<?> loadedClass, String callbackMethod) { + logPrintStream.println("! Class loaded: " + loadedClass.getCanonicalName() + + " calling hook " + callbackMethod); + + // Forward the call to callbackMethod. + final int lastPeriod = callbackMethod.lastIndexOf("."); + final String className = callbackMethod.substring(0, lastPeriod); + final String methodName = callbackMethod.substring(lastPeriod + 1); + + if (lastPeriod < 0 || className.isEmpty() || methodName.isEmpty()) { + throw new HostTestException(String.format( + "Unable to find class load hook: malformed method name \"%s\"", + callbackMethod)); + } + + Class<?> clazz = null; + try { + clazz = Class.forName(className); + } catch (Exception e) { + throw new HostTestException(String.format( + "Unable to find class load hook: Class %s not found", className), e); + } + if (!Modifier.isPublic(clazz.getModifiers())) { + throw new HostTestException(String.format( + "Unable to find class load hook: Class %s must be public", className)); + } + + Method method = null; + try { + method = clazz.getMethod(methodName, Class.class); + } catch (Exception e) { + throw new HostTestException(String.format( + "Unable to find class load hook: class %s doesn't have method %s" + + " (method must take exactly one parameter of type Class, and public static)", + className, + methodName), e); + } + if (!(Modifier.isPublic(method.getModifiers()) + && Modifier.isStatic(method.getModifiers()))) { + throw new HostTestException(String.format( + "Unable to find class load hook: Method %s in class %s must be public static", + methodName, className)); + } + try { + method.invoke(null, loadedClass); + } catch (Exception e) { + throw new HostTestException(String.format( + "Unable to invoke class load hook %s.%s", + className, + methodName), e); + } + } +} diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt new file mode 100644 index 000000000000..828d2a3e01c6 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt @@ -0,0 +1,39 @@ +# File containing standard options to HostStubGen + +--debug + +# Uncomment below lines to enable each feature. +--enable-non-stub-method-check +# --no-non-stub-method-check + +# --enable-method-logging + + +# Standard annotations. +# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`. +--stub-annotation + android.hosttest.annotation.HostSideTestStub + +--keep-annotation + android.hosttest.annotation.HostSideTestKeep + +--stub-class-annotation + android.hosttest.annotation.HostSideTestWholeClassStub + +--keep-class-annotation + android.hosttest.annotation.HostSideTestWholeClassKeep + +--throw-annotation + android.hosttest.annotation.HostSideTestThrow + +--remove-annotation + android.hosttest.annotation.HostSideTestRemove + +--substitute-annotation + android.hosttest.annotation.HostSideTestSubstitute + +--native-substitute-annotation + android.hosttest.annotation.HostSideTestNativeSubstitutionClass + +--class-load-hook-annotation + android.hosttest.annotation.HostSideTestClassLoadHook diff --git a/tools/hoststubgen/hoststubgen/jarjar-rules.txt b/tools/hoststubgen/hoststubgen/jarjar-rules.txt new file mode 100644 index 000000000000..4e61ba6f67b3 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/jarjar-rules.txt @@ -0,0 +1,2 @@ +# Rename guava +rule com.google.common.** com.android.hoststubgen.x.@0
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt new file mode 100644 index 000000000000..207ba52685f8 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen + +/** + * We will not print the stack trace for exceptions implementing it. + */ +interface UserErrorException + +/** + * Exceptions about parsing class files. + */ +class ClassParseException(message: String) : Exception(message) + +/** + * Use it for internal exception that really shouldn't happen. + */ +class HostStubGenInternalException(message: String) : Exception(message) + +/** + * Exceptions about the content in a jar file. + */ +class InvalidJarFileException(message: String) : Exception(message), UserErrorException + +/** + * Exceptions missing classes, fields, methods, etc. + */ +class UnknownApiException(message: String) : Exception(message), UserErrorException + +/** + * Exceptions related to invalid annotations -- e.g. more than one visibility annotation + * on a single API. + */ +class InvalidAnnotationException(message: String) : Exception(message), UserErrorException + diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt new file mode 100644 index 000000000000..8db4b6961376 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen + +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.filters.AnnotationBasedFilter +import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter +import com.android.hoststubgen.filters.ConstantFilter +import com.android.hoststubgen.filters.FilterPolicy +import com.android.hoststubgen.filters.ImplicitOutputFilter +import com.android.hoststubgen.filters.KeepAllClassesFilter +import com.android.hoststubgen.filters.OutputFilter +import com.android.hoststubgen.filters.StubIntersectingFilter +import com.android.hoststubgen.filters.createFilterFromTextPolicyFile +import com.android.hoststubgen.filters.printAsTextPolicy +import com.android.hoststubgen.visitors.BaseAdapter +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.util.CheckClassAdapter +import java.io.BufferedInputStream +import java.io.FileOutputStream +import java.io.InputStream +import java.io.OutputStream +import java.io.PrintWriter +import java.util.zip.ZipEntry +import java.util.zip.ZipFile +import java.util.zip.ZipOutputStream + +/** + * Actual main class. + */ +class HostStubGen(val options: HostStubGenOptions) { + fun run() { + val errors = HostStubGenErrors() + + // Load all classes. + val allClasses = loadClassStructures(options.inJar) + + // Dump the classes, if specified. + options.inputJarDumpFile?.let { + PrintWriter(it).use { pw -> allClasses.dump(pw) } + log.i("Dump file created at $it") + } + + options.inputJarAsKeepAllFile?.let { + PrintWriter(it).use { + pw -> allClasses.forEach { + classNode -> printAsTextPolicy(pw, classNode) + } + } + log.i("Dump file created at $it") + } + + // Build the filters. + val filter = buildFilter(errors, allClasses, options) + + // Transform the jar. + convert( + options.inJar, + options.outStubJar, + options.outImplJar, + filter, + options.enableClassChecker, + allClasses, + errors, + ) + } + + /** + * Load all the classes, without code. + */ + private fun loadClassStructures(inJar: String): ClassNodes { + log.i("Reading class structure from $inJar ...") + val start = System.currentTimeMillis() + + val allClasses = ClassNodes() + + log.withIndent { + ZipFile(inJar).use { inZip -> + val inEntries = inZip.entries() + + while (inEntries.hasMoreElements()) { + val entry = inEntries.nextElement() + + BufferedInputStream(inZip.getInputStream(entry)).use { bis -> + if (entry.name.endsWith(".class")) { + val cr = ClassReader(bis) + val cn = ClassNode() + cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG + or ClassReader.SKIP_FRAMES) + if (!allClasses.addClass(cn)) { + log.w("Duplicate class found: ${cn.name}") + } + } else if (entry.name.endsWith(".dex")) { + // Seems like it's an ART jar file. We can't process it. + // It's a fatal error. + throw InvalidJarFileException( + "$inJar is not a desktop jar file. It contains a *.dex file.") + } else { + // Unknown file type. Skip. + while (bis.available() > 0) { + bis.skip((1024 * 1024).toLong()) + } + } + } + } + } + } + if (allClasses.size == 0) { + log.w("$inJar contains no *.class files.") + } + + val end = System.currentTimeMillis() + log.v("Done reading class structure in %.1f second(s).", (end - start) / 1000.0) + return allClasses + } + + /** + * Build the filter, which decides what classes/methods/fields should be put in stub or impl + * jars, and "how". (e.g. with substitution?) + */ + private fun buildFilter( + errors: HostStubGenErrors, + allClasses: ClassNodes, + options: HostStubGenOptions, + ): OutputFilter { + // We build a "chain" of multiple filters here. + // + // The filters are build in from "inside", meaning the first filter created here is + // the last filter used, so it has the least precedence. + // + // So, for example, the "remove" annotation, which is handled by AnnotationBasedFilter, + // can override a class-wide annotation, which is handled by + // ClassWidePolicyPropagatingFilter, and any annotations can be overridden by the + // text-file based filter, which is handled by parseTextFilterPolicyFile. + + // The first filter is for the default policy from the command line options. + var filter: OutputFilter = ConstantFilter(options.defaultPolicy, "default-by-options") + + // Next, we need a filter that resolves "class-wide" policies. + // This is used when a member (methods, fields, nested classes) don't get any polices + // from upper filters. e.g. when a method has no annotations, then this filter will apply + // the class-wide policy, if any. (if not, we'll fall back to the above filter.) + val classWidePropagator = ClassWidePolicyPropagatingFilter(filter) + + // Next, Java annotation based filter. + filter = AnnotationBasedFilter( + errors, + allClasses, + options.stubAnnotations, + options.keepAnnotations, + options.stubClassAnnotations, + options.keepClassAnnotations, + options.throwAnnotations, + options.removeAnnotations, + options.substituteAnnotations, + options.nativeSubstituteAnnotations, + options.classLoadHookAnnotations, + classWidePropagator + ) + + // Next, "text based" filter, which allows to override polices without touching + // the target code. + options.policyOverrideFile?.let { + filter = createFilterFromTextPolicyFile(it, allClasses, filter) + } + + // If `--intersect-stub-jar` is provided, load from these jar files too. + // We use this to restrict stub APIs to public/system/test APIs, + // by intersecting with a stub jar file created by metalava. + if (options.intersectStubJars.size > 0) { + val intersectingJars = loadIntersectingJars(options.intersectStubJars) + + filter = StubIntersectingFilter(errors, intersectingJars, filter) + } + + // Apply the implicit filter. + filter = ImplicitOutputFilter(errors, allClasses, filter) + + // Optionally keep all classes. + if (options.keepAllClasses) { + filter = KeepAllClassesFilter(filter) + } + + return filter + } + + /** + * Load jar files specified with "--intersect-stub-jar". + */ + private fun loadIntersectingJars(filenames: Set<String>): Map<String, ClassNodes> { + val intersectingJars = mutableMapOf<String, ClassNodes>() + + filenames.forEach { filename -> + intersectingJars[filename] = loadClassStructures(filename) + } + return intersectingJars + } + + /** + * Convert a JAR file into "stub" and "impl" JAR files. + */ + private fun convert( + inJar: String, + outStubJar: String, + outImplJar: String, + filter: OutputFilter, + enableChecker: Boolean, + classes: ClassNodes, + errors: HostStubGenErrors, + ) { + log.i("Converting %s into [stub: %s, impl: %s] ...", inJar, outStubJar, outImplJar) + log.i("Checker is %s", if (enableChecker) "enabled" else "disabled") + + val start = System.currentTimeMillis() + + log.withIndent { + // Open the input jar file and process each entry. + ZipFile(inJar).use { inZip -> + ZipOutputStream(FileOutputStream(outStubJar)).use { stubOutStream -> + ZipOutputStream(FileOutputStream(outImplJar)).use { implOutStream -> + val inEntries = inZip.entries() + while (inEntries.hasMoreElements()) { + val entry = inEntries.nextElement() + convertSingleEntry(inZip, entry, stubOutStream, implOutStream, + filter, enableChecker, classes, errors) + } + log.i("Converted all entries.") + } + } + log.i("Created stub: $outStubJar") + log.i("Created impl: $outImplJar") + } + } + val end = System.currentTimeMillis() + log.v("Done transforming the jar in %.1f second(s).", (end - start) / 1000.0) + } + + /** + * Convert a single ZIP entry, which may or may not be a class file. + */ + private fun convertSingleEntry( + inZip: ZipFile, + entry: ZipEntry, + stubOutStream: ZipOutputStream, + implOutStream: ZipOutputStream, + filter: OutputFilter, + enableChecker: Boolean, + classes: ClassNodes, + errors: HostStubGenErrors, + ) { + log.d("Entry: %s", entry.name) + log.withIndent { + val name = entry.name + + // Just ignore all the directories. (TODO: make sure it's okay) + if (name.endsWith("/")) { + return + } + + // If it's a class, convert it. + if (name.endsWith(".class")) { + processSingleClass(inZip, entry, stubOutStream, implOutStream, filter, + enableChecker, classes, errors) + return + } + + // Handle other file types... + + // - *.uau seems to contain hidden API information. + // - *_compat_config.xml is also about compat-framework. + if (name.endsWith(".uau") || + name.endsWith("_compat_config.xml")) { + log.d("Not needed: %s", entry.name) + return + } + + // Unknown type, we just copy it to both output zip files. + // TODO: We probably shouldn't do it for stub jar? + log.v("Copying: %s", entry.name) + copyZipEntry(inZip, entry, stubOutStream) + copyZipEntry(inZip, entry, implOutStream) + } + } + + /** + * Copy a single ZIP entry to the output. + */ + private fun copyZipEntry( + inZip: ZipFile, + entry: ZipEntry, + out: ZipOutputStream, + ) { + BufferedInputStream(inZip.getInputStream(entry)).use { bis -> + // Copy unknown entries as is to the impl out. (but not to the stub out.) + val outEntry = ZipEntry(entry.name) + out.putNextEntry(outEntry) + while (bis.available() > 0) { + out.write(bis.read()) + } + out.closeEntry() + } + } + + /** + * Convert a single class to "stub" and "impl". + */ + private fun processSingleClass( + inZip: ZipFile, + entry: ZipEntry, + stubOutStream: ZipOutputStream, + implOutStream: ZipOutputStream, + filter: OutputFilter, + enableChecker: Boolean, + classes: ClassNodes, + errors: HostStubGenErrors, + ) { + val className = entry.name.replaceFirst("\\.class$".toRegex(), "") + val classPolicy = filter.getPolicyForClass(className) + if (classPolicy.policy == FilterPolicy.Remove) { + log.d("Removing class: %s %s", className, classPolicy) + return + } + // Generate stub first. + if (classPolicy.policy.needsInStub) { + log.v("Creating stub class: %s Policy: %s", className, classPolicy) + log.withIndent { + BufferedInputStream(inZip.getInputStream(entry)).use { bis -> + val newEntry = ZipEntry(entry.name) + stubOutStream.putNextEntry(newEntry) + convertClass(/*forImpl=*/false, bis, stubOutStream, filter, enableChecker, + classes, errors) + stubOutStream.closeEntry() + } + } + } + log.v("Creating impl class: %s Policy: %s", className, classPolicy) + if (classPolicy.policy.needsInImpl) { + log.withIndent { + BufferedInputStream(inZip.getInputStream(entry)).use { bis -> + val newEntry = ZipEntry(entry.name) + implOutStream.putNextEntry(newEntry) + convertClass(/*forImpl=*/true, bis, implOutStream, filter, enableChecker, + classes, errors) + implOutStream.closeEntry() + } + } + } + } + + /** + * Convert a single class to either "stub" or "impl". + */ + private fun convertClass( + forImpl: Boolean, + input: InputStream, + out: OutputStream, + filter: OutputFilter, + enableChecker: Boolean, + classes: ClassNodes, + errors: HostStubGenErrors, + ) { + val cr = ClassReader(input) + + // COMPUTE_FRAMES wouldn't be happy if code uses + val flags = ClassWriter.COMPUTE_MAXS // or ClassWriter.COMPUTE_FRAMES + val cw = ClassWriter(flags) + + // Connect to the class writer + var outVisitor: ClassVisitor = cw + if (enableChecker) { + outVisitor = CheckClassAdapter(outVisitor) + } + val visitorOptions = BaseAdapter.Options( + enablePreTrace = options.enablePreTrace, + enablePostTrace = options.enablePostTrace, + enableMethodLogging = options.enablePreTrace, + enableNonStubMethodCallDetection = options.enableNonStubMethodCallDetection, + errors = errors, + ) + outVisitor = BaseAdapter.getVisitor(classes, outVisitor, filter, forImpl, visitorOptions) + + cr.accept(outVisitor, ClassReader.EXPAND_FRAMES) + val data = cw.toByteArray() + out.write(data) + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt new file mode 100644 index 000000000000..9df04892ddbd --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen + +class HostStubGenErrors { + fun onErrorFound(message: String) { + // For now, we just throw as soon as any error is found, but eventually we should keep + // all errors and print them at the end. + throw RuntimeException(message) + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt new file mode 100644 index 000000000000..5e71a3690700 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen + +import java.io.OutputStream +import java.io.PrintStream + +val log: HostStubGenLogger = HostStubGenLogger() + +/** Logging level */ +enum class LogLevel { + None, + Error, + Warn, + Info, + Verbose, + Debug, +} + +/** Simple logging class. */ +class HostStubGenLogger( + private var out: PrintStream = System.out!!, + var level: LogLevel = LogLevel.Info, +) { + companion object { + private val sNullPrintStream: PrintStream = PrintStream(OutputStream.nullOutputStream()) + } + + private var indentLevel: Int = 0 + get() = field + set(value) { + field = value + indent = " ".repeat(value) + } + private var indent: String = "" + + fun indent() { + indentLevel++ + } + + fun unindent() { + if (indentLevel <= 0) { + throw IllegalStateException("Unbalanced unindent() call.") + } + indentLevel-- + } + + inline fun <T> withIndent(block: () -> T): T { + try { + indent() + return block() + } finally { + unindent() + } + } + + fun isEnabled(level: LogLevel): Boolean { + return level.ordinal <= this.level.ordinal + } + + private fun println(message: String) { + out.print(indent) + out.println(message) + } + + /** Log an error. */ + fun e(message: String) { + if (level.ordinal < LogLevel.Error.ordinal) { + return + } + println(message) + } + + /** Log an error. */ + fun e(format: String, vararg args: Any?) { + if (level.ordinal < LogLevel.Error.ordinal) { + return + } + e(String.format(format, *args)) + } + + /** Log a warning. */ + fun w(message: String) { + if (level.ordinal < LogLevel.Warn.ordinal) { + return + } + println(message) + } + + /** Log a warning. */ + fun w(format: String, vararg args: Any?) { + if (level.ordinal < LogLevel.Warn.ordinal) { + return + } + w(String.format(format, *args)) + } + + /** Log an info message. */ + fun i(message: String) { + if (level.ordinal < LogLevel.Info.ordinal) { + return + } + println(message) + } + + /** Log a debug message. */ + fun i(format: String, vararg args: Any?) { + if (level.ordinal < LogLevel.Warn.ordinal) { + return + } + i(String.format(format, *args)) + } + + /** Log a verbose message. */ + fun v(message: String) { + if (level.ordinal < LogLevel.Verbose.ordinal) { + return + } + println(message) + } + + /** Log a verbose message. */ + fun v(format: String, vararg args: Any?) { + if (level.ordinal < LogLevel.Verbose.ordinal) { + return + } + v(String.format(format, *args)) + } + + /** Log a debug message. */ + fun d(message: String) { + if (level.ordinal < LogLevel.Debug.ordinal) { + return + } + println(message) + } + + /** Log a debug message. */ + fun d(format: String, vararg args: Any?) { + if (level.ordinal < LogLevel.Warn.ordinal) { + return + } + d(String.format(format, *args)) + } + + inline fun forVerbose(block: () -> Unit) { + if (isEnabled(LogLevel.Verbose)) { + block() + } + } + + inline fun forDebug(block: () -> Unit) { + if (isEnabled(LogLevel.Debug)) { + block() + } + } + + /** Return a stream for error. */ + fun getErrorPrintStream(): PrintStream { + if (level.ordinal < LogLevel.Error.ordinal) { + return sNullPrintStream + } + + // TODO Apply indent + return PrintStream(out) + } + + /** Return a stream for verbose messages. */ + fun getVerbosePrintStream(): PrintStream { + if (level.ordinal < LogLevel.Verbose.ordinal) { + return sNullPrintStream + } + // TODO Apply indent + return PrintStream(out) + } + + /** Return a stream for debug messages. */ + fun getInfoPrintStream(): PrintStream { + if (level.ordinal < LogLevel.Info.ordinal) { + return sNullPrintStream + } + // TODO Apply indent + return PrintStream(out) + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt new file mode 100644 index 000000000000..9a54ecffc8c2 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen + +import com.android.hoststubgen.filters.FilterPolicy +import java.io.BufferedReader +import java.io.File +import java.io.FileReader + +/** + * Options that can be set from command line arguments. + */ +class HostStubGenOptions( + /** Input jar file*/ + var inJar: String = "", + + /** Output stub jar file */ + var outStubJar: String = "", + + /** Output implementation jar file */ + var outImplJar: String = "", + + var inputJarDumpFile: String? = null, + + var inputJarAsKeepAllFile: String? = null, + + var stubAnnotations: MutableSet<String> = mutableSetOf(), + var keepAnnotations: MutableSet<String> = mutableSetOf(), + var throwAnnotations: MutableSet<String> = mutableSetOf(), + var removeAnnotations: MutableSet<String> = mutableSetOf(), + var stubClassAnnotations: MutableSet<String> = mutableSetOf(), + var keepClassAnnotations: MutableSet<String> = mutableSetOf(), + + var substituteAnnotations: MutableSet<String> = mutableSetOf(), + var nativeSubstituteAnnotations: MutableSet<String> = mutableSetOf(), + var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(), + + var intersectStubJars: MutableSet<String> = mutableSetOf(), + + var policyOverrideFile: String? = null, + + var defaultPolicy: FilterPolicy = FilterPolicy.Remove, + var keepAllClasses: Boolean = false, + + var logLevel: LogLevel = LogLevel.Info, + + var cleanUpOnError: Boolean = false, + + var enableClassChecker: Boolean = false, + var enablePreTrace: Boolean = false, + var enablePostTrace: Boolean = false, + + var enableMethodLogging: Boolean = false, + + var enableNonStubMethodCallDetection: Boolean = true, +) { + companion object { + + private fun String.ensureFileExists(): String { + if (!File(this).exists()) { + throw InputFileNotFoundException(this) + } + return this + } + + fun parseArgs(args: Array<String>): HostStubGenOptions { + val ret = HostStubGenOptions() + + val ai = ArgIterator(expandAtFiles(args)) + + var allAnnotations = mutableSetOf<String>() + + fun ensureUniqueAnnotation(name: String): String { + if (!allAnnotations.add(name)) { + throw DuplicateAnnotationException(ai.current) + } + return name + } + + while (true) { + val arg = ai.nextArgOptional() + if (arg == null) { + break + } + + when (arg) { + // TODO: Write help + "-h", "--h" -> TODO("Help is not implemented yet") + + "-v", "--verbose" -> ret.logLevel = LogLevel.Verbose + "-d", "--debug" -> ret.logLevel = LogLevel.Debug + "-q", "--quiet" -> ret.logLevel = LogLevel.None + + "--in-jar" -> ret.inJar = ai.nextArgRequired(arg).ensureFileExists() + "--out-stub-jar" -> ret.outStubJar = ai.nextArgRequired(arg) + "--out-impl-jar" -> ret.outImplJar = ai.nextArgRequired(arg) + + "--policy-override-file" -> + ret.policyOverrideFile = ai.nextArgRequired(arg).ensureFileExists() + + "--clean-up-on-error" -> ret.cleanUpOnError = true + "--no-clean-up-on-error" -> ret.cleanUpOnError = false + + "--default-remove" -> ret.defaultPolicy = FilterPolicy.Remove + "--default-throw" -> ret.defaultPolicy = FilterPolicy.Throw + "--default-keep" -> ret.defaultPolicy = FilterPolicy.Keep + "--default-stub" -> ret.defaultPolicy = FilterPolicy.Stub + + "--keep-all-classes" -> ret.keepAllClasses = true + "--no-keep-all-classes" -> ret.keepAllClasses = false + + "--stub-annotation" -> + ret.stubAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--keep-annotation" -> + ret.keepAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--stub-class-annotation" -> + ret.stubClassAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--keep-class-annotation" -> + ret.keepClassAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--throw-annotation" -> + ret.throwAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--remove-annotation" -> + ret.removeAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--substitute-annotation" -> + ret.substituteAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--native-substitute-annotation" -> + ret.nativeSubstituteAnnotations += + ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--class-load-hook-annotation" -> + ret.classLoadHookAnnotations += + ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--intersect-stub-jar" -> + ret.intersectStubJars += ai.nextArgRequired(arg).ensureFileExists() + + "--gen-keep-all-file" -> + ret.inputJarAsKeepAllFile = ai.nextArgRequired(arg) + + // Following options are for debugging. + "--enable-class-checker" -> ret.enableClassChecker = true + "--no-class-checker" -> ret.enableClassChecker = false + + "--enable-pre-trace" -> ret.enablePreTrace = true + "--no-pre-trace" -> ret.enablePreTrace = false + + "--enable-post-trace" -> ret.enablePostTrace = true + "--no-post-trace" -> ret.enablePostTrace = false + + "--enable-method-logging" -> ret.enableMethodLogging = true + "--no-method-logging" -> ret.enableMethodLogging = false + + "--enable-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = true + "--no-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = false + + "--gen-input-dump-file" -> ret.inputJarDumpFile = ai.nextArgRequired(arg) + + else -> throw ArgumentsException("Unknown option: $arg") + } + } + if (ret.inJar.isEmpty()) { + throw ArgumentsException("Required option missing: --in-jar") + } + if (ret.outStubJar.isEmpty()) { + throw ArgumentsException("Required option missing: --out-stub-jar") + } + if (ret.outImplJar.isEmpty()) { + throw ArgumentsException("Required option missing: --out-impl-jar") + } + + return ret + } + + /** + * Scan the arguments, and if any of them starts with an `@`, then load from the file + * and use its content as arguments. + * + * In this file, each line is treated as a single argument. + * + * The file can contain '#' as comments. + */ + private fun expandAtFiles(args: Array<String>): List<String> { + val ret = mutableListOf<String>() + + args.forEach { arg -> + if (!arg.startsWith('@')) { + ret += arg + return@forEach + } + // Read from the file, and add each line to the result. + val filename = arg.substring(1).ensureFileExists() + + log.v("Expanding options file $filename") + + BufferedReader(FileReader(filename)).use { reader -> + while (true) { + var line = reader.readLine() + if (line == null) { + break // EOF + } + + line = normalizeTextLine(line) + if (line.isNotEmpty()) { + ret += line + } + } + } + } + return ret + } + } + + open class ArgumentsException(message: String?) : Exception(message), UserErrorException + + /** Thrown when the same annotation is used with different annotation arguments. */ + class DuplicateAnnotationException(annotationName: String?) : + ArgumentsException("Duplicate annotation specified: '$annotationName'") + + /** Thrown when an input file does not exist. */ + class InputFileNotFoundException(filename: String) : + ArgumentsException("File '$filename' not found") + + private class ArgIterator( + private val args: List<String>, + private var currentIndex: Int = -1 + ) { + val current: String + get() = args.get(currentIndex) + + /** + * Get the next argument, or [null] if there's no more arguments. + */ + fun nextArgOptional(): String? { + if ((currentIndex + 1) >= args.size) { + return null + } + return args.get(++currentIndex) + } + + /** + * Get the next argument, or throw if + */ + fun nextArgRequired(argName: String): String { + nextArgOptional().let { + if (it == null) { + throw ArgumentsException("Missing parameter for option $argName") + } + if (it.isEmpty()) { + throw ArgumentsException("Parameter can't be empty for option $argName") + } + return it + } + } + } + + override fun toString(): String { + return """ + HostStubGenOptions{ + inJar='$inJar', + outStubJar='$outStubJar', + outImplJar='$outImplJar', + inputJarDumpFile=$inputJarDumpFile, + inputJarAsKeepAllFile=$inputJarAsKeepAllFile, + stubAnnotations=$stubAnnotations, + keepAnnotations=$keepAnnotations, + throwAnnotations=$throwAnnotations, + removeAnnotations=$removeAnnotations, + stubClassAnnotations=$stubClassAnnotations, + keepClassAnnotations=$keepClassAnnotations, + substituteAnnotations=$substituteAnnotations, + nativeSubstituteAnnotations=$nativeSubstituteAnnotations, + classLoadHookAnnotations=$classLoadHookAnnotations, + intersectStubJars=$intersectStubJars, + policyOverrideFile=$policyOverrideFile, + defaultPolicy=$defaultPolicy, + keepAllClasses=$keepAllClasses, + logLevel=$logLevel, + cleanUpOnError=$cleanUpOnError, + enableClassChecker=$enableClassChecker, + enablePreTrace=$enablePreTrace, + enablePostTrace=$enablePostTrace, + enableMethodLogging=$enableMethodLogging, + enableNonStubMethodCallDetection=$enableNonStubMethodCallDetection, + } + """.trimIndent() + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt new file mode 100644 index 000000000000..0321d9db03ed --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:JvmName("Main") + +package com.android.hoststubgen + +const val COMMAND_NAME = "HostStubGen" + +/** + * Entry point. + */ +fun main(args: Array<String>) { + var success = false + var clanupOnError = false + try { + // Parse the command line arguments. + val options = HostStubGenOptions.parseArgs(args) + clanupOnError = options.cleanUpOnError + + log.level = options.logLevel + + log.v("HostStubGen started") + log.v("Options: $options") + + // Run. + HostStubGen(options).run() + + success = true + } catch (e: Exception) { + log.e("$COMMAND_NAME: Error: ${e.message}") + if (e !is UserErrorException) { + e.printStackTrace(log.getErrorPrintStream()) + } + if (clanupOnError) { + TODO("clanupOnError is not implemented yet") + } + } + + log.v("HostStubGen finished") + + System.exit(if (success) 0 else 1 ) +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt new file mode 100644 index 000000000000..9fbd6d09bfb0 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen + +/** + * A regex that maches whitespate. + */ +val whitespaceRegex = """\s+""".toRegex() + +/** + * Remove the comment ('#' and following) and surrounding whitespace from a line. + */ +fun normalizeTextLine(s: String): String { + // Remove # and after. (comment) + val pos = s.indexOf('#') + val uncommented = if (pos < 0) s else s.substring(0, pos) + + // Remove surrounding whitespace. + return uncommented.trim() +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt new file mode 100644 index 000000000000..a51bdcf0c793 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.asm + +import com.android.hoststubgen.ClassParseException +import com.android.hoststubgen.HostStubGenInternalException +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type +import org.objectweb.asm.tree.AnnotationNode +import org.objectweb.asm.tree.ClassNode + + +/** Name of the class initializer method. */ +val CLASS_INITIALIZER_NAME = "<clinit>" + +/** Descriptor of the class initializer method. */ +val CLASS_INITIALIZER_DESC = "()V" + +/** + * Find any of [anyAnnotations] from the list of visible / invisible annotations. + */ +fun findAnyAnnotation( + anyAnnotations: Set<String>, + visibleAnnotations: List<AnnotationNode>?, + invisibleAnnotations: List<AnnotationNode>?, + ): AnnotationNode? { + for (an in visibleAnnotations ?: emptyList()) { + if (anyAnnotations.contains(an.desc)) { + return an + } + } + for (an in invisibleAnnotations ?: emptyList()) { + if (anyAnnotations.contains(an.desc)) { + return an + } + } + return null +} + +fun findAnnotationValueAsString(an: AnnotationNode, propertyName: String): String? { + for (i in 0..(an.values?.size ?: 0) - 2 step 2) { + val name = an.values[i] + + if (name != propertyName) { + continue + } + val value = an.values[i + 1] + if (value is String) { + return value + } + throw ClassParseException( + "The type of '$name' in annotation \"${an.desc}\" must be String" + + ", but is ${value?.javaClass?.canonicalName}") + } + return null +} + +private val removeLastElement = """[./][^./]*$""".toRegex() + +fun getPackageNameFromClassName(className: String): String { + return className.replace(removeLastElement, "") +} + +fun resolveClassName(className: String, packageName: String): String { + if (className.contains('.') || className.contains('/')) { + return className + } + return "$packageName.$className" +} + +fun String.toJvmClassName(): String { + return this.replace('.', '/') +} + +fun String.toHumanReadableClassName(): String { + return this.replace('/', '.') +} + +fun String.toHumanReadableMethodName(): String { + return this.replace('/', '.') +} + +private val numericalInnerClassName = """.*\$\d+$""".toRegex() + +fun isAnonymousInnerClass(cn: ClassNode): Boolean { + // TODO: Is there a better way? + return cn.name.matches(numericalInnerClassName) +} + +/** + * Take a class name. If it's a nested class, then return the name of its direct outer class name. + * Otherwise, return null. + */ +fun getDirectOuterClassName(className: String): String? { + val pos = className.indexOf('$') + if (pos < 0) { + return null + } + return className.substring(0, pos) +} + +/** + * Write bytecode to push all the method arguments to the stack. + * The number of arguments and their type are taken from [methodDescriptor]. + */ +fun writeByteCodeToPushArguments(methodDescriptor: String, writer: MethodVisitor) { + var i = -1 + Type.getArgumentTypes(methodDescriptor).forEach { type -> + i++ + + // See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions + + // Note, long and double will consume two local variable spaces, so the extra `i++`. + when (type) { + Type.VOID_TYPE -> throw HostStubGenInternalException("VOID_TYPE not expected") + Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE + -> writer.visitVarInsn(Opcodes.ILOAD, i) + Type.LONG_TYPE -> writer.visitVarInsn(Opcodes.LLOAD, i++) + Type.FLOAT_TYPE -> writer.visitVarInsn(Opcodes.FLOAD, i) + Type.DOUBLE_TYPE -> writer.visitVarInsn(Opcodes.DLOAD, i++) + else -> writer.visitVarInsn(Opcodes.ALOAD, i) + } + } +} + +/** + * Write bytecode to "RETURN" that matches the method's return type, according to + * [methodDescriptor]. + */ +fun writeByteCodeToReturn(methodDescriptor: String, writer: MethodVisitor) { + Type.getReturnType(methodDescriptor).let { type -> + // See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions + when (type) { + Type.VOID_TYPE -> writer.visitInsn(Opcodes.RETURN) + Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE + -> writer.visitInsn(Opcodes.IRETURN) + Type.LONG_TYPE -> writer.visitInsn(Opcodes.LRETURN) + Type.FLOAT_TYPE -> writer.visitInsn(Opcodes.FRETURN) + Type.DOUBLE_TYPE -> writer.visitInsn(Opcodes.DRETURN) + else -> writer.visitInsn(Opcodes.ARETURN) + } + } +} + +/** + * Return the "visibility" modifier from an `access` integer. + * + * (see https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1) + */ +fun getVisibilityModifier(access: Int): Int { + return access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED) +} + +/** + * Return true if an `access` integer is "private" or "package private". + */ +fun isVisibilityPrivateOrPackagePrivate(access: Int): Boolean { + return when (getVisibilityModifier(access)) { + 0 -> true // Package private. + Opcodes.ACC_PRIVATE -> true + else -> false + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt new file mode 100644 index 000000000000..4df0bfc4a8d1 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt @@ -0,0 +1,149 @@ +package com.android.hoststubgen.asm + +import com.android.hoststubgen.ClassParseException +import org.objectweb.asm.tree.AnnotationNode +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.FieldNode +import org.objectweb.asm.tree.MethodNode +import org.objectweb.asm.tree.TypeAnnotationNode +import java.io.PrintWriter +import java.util.Arrays + +/** + * Stores all classes loaded from a jar file, in a form of [ClassNode] + */ +class ClassNodes { + val mAllClasses: MutableMap<String, ClassNode> = HashMap() + + /** + * Total number of classes registered. + */ + val size: Int + get() = mAllClasses.size + + /** Add a [ClassNode] */ + fun addClass(cn: ClassNode): Boolean { + if (mAllClasses.containsKey(cn.name)) { + return false + } + mAllClasses[cn.name.toJvmClassName()] = cn + return true + } + + /** Get a class's [ClassNodes] (which may not exist) */ + fun findClass(name: String): ClassNode? { + return mAllClasses[name.toJvmClassName()] + } + + /** Get a class's [ClassNodes] (which must exists) */ + fun getClass(name: String): ClassNode { + return findClass(name) ?: throw ClassParseException("Class $name not found") + } + + /** Find a field, which may not exist. */ + fun findField( + className: String, + fieldName: String, + ): FieldNode? { + return findClass(className)?.fields?.firstOrNull { it.name == fieldName }?.let { fn -> + return fn + } + } + + /** Find a method, which may not exist. */ + fun findMethod( + className: String, + methodName: String, + descriptor: String, + ): MethodNode? { + return findClass(className)?.methods + ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn -> + return mn + } + } + + /** @return true if a class has a class initializer. */ + fun hasClassInitializer(className: String): Boolean { + return findMethod(className, CLASS_INITIALIZER_NAME, CLASS_INITIALIZER_DESC) != null + } + + /** Run the lambda on each class in alphabetical order. */ + fun forEach(consumer: (classNode: ClassNode) -> Unit) { + val keys = mAllClasses.keys.toTypedArray() + Arrays.sort(keys) + + for (name in keys) { + consumer(mAllClasses[name]!!) + } + } + + /** + * Dump all classes. + */ + fun dump(pw: PrintWriter) { + forEach { classNode -> dumpClass(pw, classNode) } + } + + private fun dumpClass(pw: PrintWriter, cn: ClassNode) { + pw.printf("Class: %s [access: %x]\n", cn.name, cn.access) + dumpAnnotations(pw, " ", + cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations, + cn.visibleAnnotations, cn.invisibleAnnotations, + ) + + for (f in cn.fields ?: emptyList()) { + pw.printf(" Field: %s [sig: %s] [desc: %s] [access: %x]\n", + f.name, f.signature, f.desc, f.access) + dumpAnnotations(pw, " ", + f.visibleTypeAnnotations, f.invisibleTypeAnnotations, + f.visibleAnnotations, f.invisibleAnnotations, + ) + } + for (m in cn.methods ?: emptyList()) { + pw.printf(" Method: %s [sig: %s] [desc: %s] [access: %x]\n", + m.name, m.signature, m.desc, m.access) + dumpAnnotations(pw, " ", + m.visibleTypeAnnotations, m.invisibleTypeAnnotations, + m.visibleAnnotations, m.invisibleAnnotations, + ) + } + } + + private fun dumpAnnotations( + pw: PrintWriter, + prefix: String, + visibleTypeAnnotations: List<TypeAnnotationNode>?, + invisibleTypeAnnotations: List<TypeAnnotationNode>?, + visibleAnnotations: List<AnnotationNode>?, + invisibleAnnotations: List<AnnotationNode>?, + ) { + for (an in visibleTypeAnnotations ?: emptyList()) { + pw.printf("%sTypeAnnotation(vis): %s\n", prefix, an.desc) + } + for (an in invisibleTypeAnnotations ?: emptyList()) { + pw.printf("%sTypeAnnotation(inv): %s\n", prefix, an.desc) + } + for (an in visibleAnnotations ?: emptyList()) { + pw.printf("%sAnnotation(vis): %s\n", prefix, an.desc) + if (an.values == null) { + continue + } + var i = 0 + while (i < an.values.size - 1) { + pw.printf("%s - %s -> %s \n", prefix, an.values[i], an.values[i + 1]) + i += 2 + } + } + for (an in invisibleAnnotations ?: emptyList()) { + pw.printf("%sAnnotation(inv): %s\n", prefix, an.desc) + if (an.values == null) { + continue + } + var i = 0 + while (i < an.values.size - 1) { + pw.printf("%s - %s -> %s \n", prefix, an.values[i], an.values[i + 1]) + i += 2 + } + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt new file mode 100644 index 000000000000..454569d2f1c5 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.ClassParseException +import com.android.hoststubgen.HostStubGenErrors +import com.android.hoststubgen.HostStubGenInternalException +import com.android.hoststubgen.InvalidAnnotationException +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.findAnnotationValueAsString +import com.android.hoststubgen.asm.findAnyAnnotation +import com.android.hoststubgen.asm.toHumanReadableMethodName +import com.android.hoststubgen.asm.toJvmClassName +import com.android.hoststubgen.log +import org.objectweb.asm.tree.AnnotationNode +import org.objectweb.asm.tree.ClassNode + +// TODO: Detect invalid cases, such as... +// - Class's visibility is lower than the members'. +// - HostSideTestSubstituteWith is set, but it doesn't have @Stub or @Keep + +/** + * [OutputFilter] using Java annotations. + */ +class AnnotationBasedFilter( + private val errors: HostStubGenErrors, + private val classes: ClassNodes, + stubAnnotations_: Set<String>, + keepAnnotations_: Set<String>, + stubClassAnnotations_: Set<String>, + keepClassAnnotations_: Set<String>, + throwAnnotations_: Set<String>, + removeAnnotations_: Set<String>, + substituteAnnotations_: Set<String>, + nativeSubstituteAnnotations_: Set<String>, + classLoadHookAnnotations_: Set<String>, + fallback: OutputFilter, +) : DelegatingFilter(fallback) { + private var stubAnnotations = convertToInternalNames(stubAnnotations_) + private var keepAnnotations = convertToInternalNames(keepAnnotations_) + private var stubClassAnnotations = convertToInternalNames(stubClassAnnotations_) + private var keepClassAnnotations = convertToInternalNames(keepClassAnnotations_) + private var throwAnnotations = convertToInternalNames(throwAnnotations_) + private var removeAnnotations = convertToInternalNames(removeAnnotations_) + private var substituteAnnotations = convertToInternalNames(substituteAnnotations_) + private var nativeSubstituteAnnotations = convertToInternalNames(nativeSubstituteAnnotations_) + private var classLoadHookAnnotations = convertToInternalNames(classLoadHookAnnotations_) + + /** Annotations that control API visibility. */ + private var visibilityAnnotations: Set<String> = convertToInternalNames( + stubAnnotations_ + + keepAnnotations_ + + stubClassAnnotations_ + + keepClassAnnotations_ + + throwAnnotations_ + + removeAnnotations_) + + /** + * All the annotations we use. Note, this one is in a [convertToJvmNames] format unlike + * other ones, because of how it's used. + */ + private var allAnnotations: Set<String> = convertToJvmNames( + stubAnnotations_ + + keepAnnotations_ + + stubClassAnnotations_ + + keepClassAnnotations_ + + throwAnnotations_ + + removeAnnotations_ + + substituteAnnotations_ + + nativeSubstituteAnnotations_ + + classLoadHookAnnotations_) + + private val substitutionHelper = SubstitutionHelper() + + private val reasonAnnotation = "annotation" + private val reasonClassAnnotation = "class-annotation" + + /** + * Throw if an item has more than one visibility annotations. + * + * name1 - 4 are only used in exception messages. We take them as separate strings + * to avoid unnecessary string concatenations. + */ + private fun detectInvalidAnnotations( + visibles: List<AnnotationNode>?, + invisibles: List<AnnotationNode>?, + type: String, + name1: String, + name2: String, + name3: String, + ) { + var count = 0 + for (an in visibles ?: emptyList()) { + if (visibilityAnnotations.contains(an.desc)) { + count++ + } + } + for (an in invisibles ?: emptyList()) { + if (visibilityAnnotations.contains(an.desc)) { + count++ + } + } + if (count > 1) { + val description = if (name2 == "" && name3 == "") { + "$type $name1" + } else { + "$type $name1.$name2$name3" + } + throw InvalidAnnotationException( + "Found more than one visibility annotations on $description") + } + } + + /** + * Find a visibility annotation. + * + * name1 - 4 are only used in exception messages. + */ + private fun findAnnotation( + visibles: List<AnnotationNode>?, + invisibles: List<AnnotationNode>?, + type: String, + name1: String, + name2: String = "", + name3: String = "", + ): FilterPolicyWithReason? { + detectInvalidAnnotations(visibles, invisibles, type, name1, name2, name3) + + findAnyAnnotation(stubAnnotations, visibles, invisibles)?.let { + return FilterPolicy.Stub.withReason(reasonAnnotation) + } + findAnyAnnotation(stubClassAnnotations, visibles, invisibles)?.let { + return FilterPolicy.StubClass.withReason(reasonClassAnnotation) + } + findAnyAnnotation(keepAnnotations, visibles, invisibles)?.let { + return FilterPolicy.Keep.withReason(reasonAnnotation) + } + findAnyAnnotation(keepClassAnnotations, visibles, invisibles)?.let { + return FilterPolicy.KeepClass.withReason(reasonClassAnnotation) + } + findAnyAnnotation(throwAnnotations, visibles, invisibles)?.let { + return FilterPolicy.Throw.withReason(reasonAnnotation) + } + findAnyAnnotation(removeAnnotations, visibles, invisibles)?.let { + return FilterPolicy.Remove.withReason(reasonAnnotation) + } + return null + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + val cn = classes.getClass(className) + + findAnnotation( + cn.visibleAnnotations, + cn.invisibleAnnotations, + "class", + className)?.let { + return it + } + + // If it's any of the annotations, then always keep it. + if (allAnnotations.contains(className)) { + return FilterPolicy.KeepClass.withReason("HostStubGen Annotation") + } + + return super.getPolicyForClass(className) + } + + override fun getPolicyForField( + className: String, + fieldName: String + ): FilterPolicyWithReason { + val cn = classes.getClass(className) + + cn.fields?.firstOrNull { it.name == fieldName }?.let {fn -> + findAnnotation( + fn.visibleAnnotations, + fn.invisibleAnnotations, + "field", + className, + fieldName + )?.let { policy -> + // If the item has an annotation, then use it. + return policy + } + } + return super.getPolicyForField(className, fieldName) + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String + ): FilterPolicyWithReason { + val cn = classes.getClass(className) + + cn.methods?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn -> + // @SubstituteWith is going to complicate the policy here, so we ask helper + // what to do. + substitutionHelper.getPolicyFromSubstitution(cn, mn.name, mn.desc)?.let { + return it + } + + // If there's no substitution, then we check the annotation. + findAnnotation( + mn.visibleAnnotations, + mn.invisibleAnnotations, + "method", + className, + methodName, + descriptor + )?.let { policy -> + return policy + } + } + return super.getPolicyForMethod(className, methodName, descriptor) + } + + override fun getRenameTo( + className: String, + methodName: String, + descriptor: String + ): String? { + val cn = classes.getClass(className) + + // If the method has a "substitute with" annotation, then return its "value" parameter. + cn.methods?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn -> + return substitutionHelper.getRenameTo(cn, mn.name, mn.desc) + } + return null + } + + override fun getNativeSubstitutionClass(className: String): String? { + classes.getClass(className).let { cn -> + findAnyAnnotation(nativeSubstituteAnnotations, + cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an -> + return getAnnotationField(an, "value")?.toJvmClassName() + } + } + return null + } + + override fun getClassLoadHook(className: String): String? { + classes.getClass(className).let { cn -> + findAnyAnnotation(classLoadHookAnnotations, + cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an -> + return getAnnotationField(an, "value")?.toHumanReadableMethodName() + } + } + return null + } + + private data class MethodKey(val name: String, val desc: String) + + /** + * In order to handle substitution, we need to build a reverse mapping of substitution + * methods. + * + * This class automatically builds such a map internally that the above methods can + * take advantage of. + */ + private inner class SubstitutionHelper { + private var currentClass: ClassNode? = null + + private var policiesFromSubstitution = mutableMapOf<MethodKey, FilterPolicyWithReason>() + private var substituteToMethods = mutableMapOf<MethodKey, String>() + + fun getPolicyFromSubstitution(cn: ClassNode, methodName: String, descriptor: String): + FilterPolicyWithReason? { + setClass(cn) + return policiesFromSubstitution[MethodKey(methodName, descriptor)] + } + + fun getRenameTo(cn: ClassNode, methodName: String, descriptor: String): String? { + setClass(cn) + return substituteToMethods[MethodKey(methodName, descriptor)] + } + + /** + * Every time we see a different class, we scan all its methods for substitution attributes, + * and compute (implicit) policies caused by them. + * + * For example, for the following methods: + * + * @Stub + * @Substitute(suffix = "_host") + * private void foo() { + * // This isn't supported on the host side. + * } + * private void foo_host() { + * // Host side implementation + * } + * + * We internally handle them as: + * + * foo() -> Remove + * foo_host() -> Stub, and then rename it to foo(). + */ + private fun setClass(cn: ClassNode) { + if (currentClass == cn) { + return + } + // If the class is changing, we'll rebuild the internal structure. + currentClass = cn + + policiesFromSubstitution.clear() + substituteToMethods.clear() + + for (mn in cn.methods ?: emptyList()) { + findAnyAnnotation(substituteAnnotations, + mn.visibleAnnotations, + mn.invisibleAnnotations)?.let { an -> + + // Find the policy for this method. + val policy = outermostFilter.getPolicyForMethod(cn.name, mn.name, mn.desc) + .policy.resolveClassWidePolicy() + // Make sure it's either Stub or Keep. + if (!(policy.needsInStub || policy.needsInImpl)) { + // TODO: Use the real annotation names in the message + errors.onErrorFound("@SubstituteWith must have either @Stub or @Keep") + return@let + } + if (!policy.isUsableWithMethods) { + throw HostStubGenInternalException("Policy $policy shouldn't show up here") + } + + val suffix = getAnnotationField(an, "suffix") ?: return@let + val renameFrom = mn.name + suffix + val renameTo = mn.name + + if (renameFrom == renameTo) { + errors.onErrorFound("@SubstituteWith have a different name") + return@let + } + + // This mn has "SubstituteWith". This means, + // 1. Re move the "rename-to" method, so add it to substitutedMethods. + policiesFromSubstitution[MethodKey(renameTo, mn.desc)] = + FilterPolicy.Remove.withReason("substitute-to") + + // 2. We also keep the from-to in the map. + policiesFromSubstitution[MethodKey(renameFrom, mn.desc)] = + policy.withReason("substitute-from") + substituteToMethods[MethodKey(renameFrom, mn.desc)] = renameTo + + log.v("Substitution found: %s%s -> %s", renameFrom, mn.desc, renameTo) + } + } + } + } + + /** + * Return the (String) value of 'value' parameter from an annotation. + */ + private fun getAnnotationField(an: AnnotationNode, name: String): String? { + try { + val suffix = findAnnotationValueAsString(an, name) + if (suffix == null) { + errors.onErrorFound("Annotation \"${an.desc}\" must have field $name") + } + return suffix + } catch (e: ClassParseException) { + errors.onErrorFound(e.message!!) + return null + } + } + + companion object { + /** + * Convert from human-readable type names (e.g. "com.android.TypeName") to the internal type + * names (e.g. "Lcom/android/TypeName). + */ + private fun convertToInternalNames(input: Set<String>): Set<String> { + val ret = mutableSetOf<String>() + input.forEach { ret.add("L" + it.toJvmClassName() + ";") } + return ret + } + + /** + * Convert from human-readable type names to JVM type names. + */ + private fun convertToJvmNames(input: Set<String>): Set<String> { + val ret = mutableSetOf<String>() + input.forEach { ret.add(it.toJvmClassName()) } + return ret + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt new file mode 100644 index 000000000000..6aac3d88b8b1 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.asm.getDirectOuterClassName + +/** + * This is used as the second last fallback filter. This filter propagates the class-wide policy + * (obtained from [outermostFilter]) to the fields and methods. + */ +class ClassWidePolicyPropagatingFilter( + fallback: OutputFilter, + ) : DelegatingFilter(fallback) { + + private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? { + var currentClass = className + + while (true) { + outermostFilter.getPolicyForClass(className).let { policy -> + if (policy.policy.isClassWidePolicy) { + val p = if (resolve) policy.policy.resolveClassWidePolicy() else policy.policy + + return p.withReason(policy.reason).wrapReason("class-wide in $currentClass") + } + // If the class's policy is remove, then remove it. + if (policy.policy == FilterPolicy.Remove) { + return FilterPolicy.Remove.withReason("class-wide in $currentClass") + } + } + + // Next, look at the outer class... + val outer = getDirectOuterClassName(currentClass) + if (outer == null) { + return null + } + currentClass = outer + } + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + // If it's a nested class, use the outer class's policy. + getDirectOuterClassName(className)?.let { outerName -> + getClassWidePolicy(outerName, resolve = false)?.let { policy -> + return policy + } + } + + return super.getPolicyForClass(className) + } + + override fun getPolicyForField( + className: String, + fieldName: String + ): FilterPolicyWithReason { + return getClassWidePolicy(className, resolve = true) + ?: super.getPolicyForField(className, fieldName) + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String + ): FilterPolicyWithReason { + return getClassWidePolicy(className, resolve = true) + ?: super.getPolicyForMethod(className, methodName, descriptor) + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt new file mode 100644 index 000000000000..33010baaf894 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.HostStubGenInternalException + + +/** + * [OutputFilter] with a given policy. Used to represent the default policy. + * + * This is used as the last fallback filter. + * + * @param policy the policy. Cannot be a "substitute" policy. + */ +class ConstantFilter( + policy: FilterPolicy, + val reason: String +) : OutputFilter() { + val classPolicy: FilterPolicy + val fieldPolicy: FilterPolicy + val methodPolicy: FilterPolicy + + init { + if (policy.isSubstitute) { + throw HostStubGenInternalException( + "ConstantFilter doesn't allow substitution policies.") + } + if (policy.isClassWidePolicy) { + // We prevent it, because there's no point in using class-wide policies because + // all members get othe same policy too anyway. + throw HostStubGenInternalException( + "ConstantFilter doesn't allow class-wide policies.") + } + methodPolicy = policy + + // TODO: Need to think about the realistic default behavior. + classPolicy = if (policy != FilterPolicy.Throw) policy else FilterPolicy.Remove + fieldPolicy = classPolicy + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + return classPolicy.withReason(reason) + } + + override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason { + return fieldPolicy.withReason(reason) + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String, + ): FilterPolicyWithReason { + return methodPolicy.withReason(reason) + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt new file mode 100644 index 000000000000..f0763c4ba097 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +/** + * Base class for an [OutputFilter] that uses another filter as a fallback. + */ +abstract class DelegatingFilter( + // fallback shouldn't be used by subclasses, so make it private. + // They should instead be calling into `super` or `outermostFilter`. + private val fallback: OutputFilter +) : OutputFilter() { + init { + fallback.outermostFilter = this + } + + override var outermostFilter: OutputFilter = this + get() = field + set(value) { + field = value + // Propagate the inner filters. + fallback.outermostFilter = value + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + return fallback.getPolicyForClass(className) + } + + override fun getPolicyForField( + className: String, + fieldName: String + ): FilterPolicyWithReason { + return fallback.getPolicyForField(className, fieldName) + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String + ): FilterPolicyWithReason { + return fallback.getPolicyForMethod(className, methodName, descriptor) + } + + override fun getRenameTo( + className: String, + methodName: String, + descriptor: String + ): String? { + return fallback.getRenameTo(className, methodName, descriptor) + } + + override fun getNativeSubstitutionClass(className: String): String? { + return fallback.getNativeSubstitutionClass(className) + } + + override fun getClassLoadHook(className: String): String? { + return fallback.getClassLoadHook(className) + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt new file mode 100644 index 000000000000..f11ac2f7325d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +enum class FilterPolicy { + /** + * Keep the item in the stub jar file, so tests can use it. + */ + Stub, + + /** + * Keep the item in the impl jar file, but not in the stub file. Tests cannot use it directly, + * but indirectly. + */ + Keep, + + /** + * Only used for types. Keep the class in the stub, and also all its members. + * But each member can have another annotations to override it. + */ + StubClass, + + /** + * Only used for types. Keep the class in the impl, not in the stub, and also all its members. + * But each member can have another annotations to override it. + */ + KeepClass, + + /** + * Same as [Stub], but replace it with a "substitution" method. Only usable with methods. + */ + SubstituteAndStub, + + /** + * Same as [Keep], but replace it with a "substitution" method. Only usable with methods. + */ + SubstituteAndKeep, + + /** + * Only usable with methods. The item will be kept in the impl jar file, but when called, + * it'll throw. + */ + Throw, + + /** + * Remove the item completely. + */ + Remove; + + val isSubstitute: Boolean + get() = this == SubstituteAndStub || this == SubstituteAndKeep + + val needsInStub: Boolean + get() = this == Stub || this == StubClass || this == SubstituteAndStub + + val needsInImpl: Boolean + get() = this != Remove + + /** Returns whether a policy can be used with classes */ + val isUsableWithClasses: Boolean + get() { + return when (this) { + Stub, StubClass, Keep, KeepClass, Remove -> true + else -> false + } + } + + /** Returns whether a policy can be used with fields. */ + val isUsableWithFields: Boolean + get() { + return when (this) { + Stub, Keep, Remove -> true + else -> false + } + } + + /** Returns whether a policy can be used with methods */ + val isUsableWithMethods: Boolean + get() { + return when (this) { + StubClass, KeepClass -> false + else -> true + } + } + + /** Returns whether a policy is a class-wide one. */ + val isClassWidePolicy: Boolean + get() { + return when (this) { + StubClass, KeepClass -> true + else -> false + } + } + + fun getSubstitutionBasePolicy(): FilterPolicy { + return when (this) { + SubstituteAndKeep -> Keep + SubstituteAndStub -> Stub + else -> this + } + } + + /** + * Convert {Stub,Keep}Class to the corresponding Stub or Keep. + */ + fun resolveClassWidePolicy(): FilterPolicy { + return when (this) { + StubClass -> Stub + KeepClass -> Keep + else -> this + } + } + + /** + * Create a [FilterPolicyWithReason] with a given reason. + */ + fun withReason(reason: String): FilterPolicyWithReason { + return FilterPolicyWithReason(this, reason) + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt new file mode 100644 index 000000000000..b64a2f5fd8a5 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +/** + * Captures a [FilterPolicy] with a human-readable reason. + */ +data class FilterPolicyWithReason ( + val policy: FilterPolicy, + val reason: String = "", +) { + /** + * Return a new [FilterPolicy] with an updated reason, while keeping the original reason + * as an "inner-reason". + */ + fun wrapReason(reason: String): FilterPolicyWithReason { + return FilterPolicyWithReason(policy, "$reason [inner-reason: ${this.reason}]") + } + + /** + * If the visibility is lower than "Keep" (meaning if it's "remove"), + * then return a new [FilterPolicy] with "Keep". + * Otherwise, return itself + */ + fun promoteToKeep(promotionReason: String): FilterPolicyWithReason { + if (policy.needsInImpl) { + return this + } + val newPolicy = if (policy.isClassWidePolicy) FilterPolicy.KeepClass else FilterPolicy.Keep + + return FilterPolicyWithReason(newPolicy, + "$promotionReason [original remove reason: ${this.reason}]") + } + + /** + * If the visibility is above "Keep" (meaning if it's "stub"), + * then return a new [FilterPolicy] with "Keep". + * Otherwise, return itself + */ + fun demoteToKeep(promotionReason: String): FilterPolicyWithReason { + if (!policy.needsInStub) { + return this + } + val newPolicy = if (policy.isClassWidePolicy) FilterPolicy.KeepClass else FilterPolicy.Keep + + return FilterPolicyWithReason(newPolicy, + "$promotionReason [original stub reason: ${this.reason}]") + } + + override fun toString(): String { + return "[$policy - reason: $reason]" + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt new file mode 100644 index 000000000000..9c372ff68e37 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.HostStubGenErrors +import com.android.hoststubgen.HostStubGenInternalException +import com.android.hoststubgen.asm.isAnonymousInnerClass +import com.android.hoststubgen.log +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate + +/** + * Filter implementing "implicit" rules, such as: + * - "keep all anonymous inner classes if the outer class is keep". + * (But anonymous inner classes should never be in "stub") + * - For classes in stub, make sure private parameterless constructors are also in stub, if any. + */ +class ImplicitOutputFilter( + private val errors: HostStubGenErrors, + private val classes: ClassNodes, + fallback: OutputFilter +) : DelegatingFilter(fallback) { + private fun getClassImplicitPolicy(className: String): FilterPolicyWithReason? { + // TODO: This check should be cached. + val cn = classes.getClass(className) + + if (isAnonymousInnerClass(cn)) { + log.forDebug { +// log.d(" anon-inner class: ${className} outer: ${cn.outerClass} ") + } + if (cn.outerClass == null) { + throw HostStubGenInternalException( + "outerClass is null for anonymous inner class") + } + // If the outer class needs to be in impl, it should be in impl too. + val outerPolicy = outermostFilter.getPolicyForClass(cn.outerClass) + if (outerPolicy.policy.needsInImpl) { + return FilterPolicy.KeepClass.withReason("anonymous-inner-class") + } + } + return null + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + // Use the implicit policy, if any. + getClassImplicitPolicy(className)?.let { return it } + + return super.getPolicyForClass(className) + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String + ): FilterPolicyWithReason { + val fallback = super.getPolicyForMethod(className, methodName, descriptor) + + // If the class is in the stub, then we need to put the private constructor in the stub too, + // to prevent the class from getting instantiated. + if (outermostFilter.getPolicyForClass(className).policy.needsInStub && + !fallback.policy.needsInStub && + (methodName == "<init>") && // Constructor? + (descriptor == "()V")) { // Has zero parameters? + classes.findMethod(className, methodName, descriptor)?.let { mn -> + if (isVisibilityPrivateOrPackagePrivate(mn.access)) { + return FilterPolicy.Stub.withReason("private constructor in stub class") + } + } + } + + return fallback + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt new file mode 100644 index 000000000000..f3551d49bd36 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.UnknownApiException +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.toHumanReadableClassName +import com.android.hoststubgen.asm.toHumanReadableMethodName + +// TODO: Validate all input names. + +class InMemoryOutputFilter( + private val classes: ClassNodes, + fallback: OutputFilter, +) : DelegatingFilter(fallback) { + private val mPolicies: MutableMap<String, FilterPolicyWithReason> = mutableMapOf() + private val mRenames: MutableMap<String, String> = mutableMapOf() + private val mNativeSubstitutionClasses: MutableMap<String, String> = mutableMapOf() + private val mClassLoadHooks: MutableMap<String, String> = mutableMapOf() + + private fun getClassKey(className: String): String { + return className.toHumanReadableClassName() + } + + private fun getFieldKey(className: String, fieldName: String): String { + return getClassKey(className) + "." + fieldName + } + + private fun getMethodKey(className: String, methodName: String, signature: String): String { + return getClassKey(className) + "." + methodName + ";" + signature + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + return mPolicies[getClassKey(className)] ?: super.getPolicyForClass(className) + } + + private fun ensureClassExists(className: String) { + if (classes.findClass(className) == null) { + throw UnknownApiException("Unknown class $className") + } + } + + private fun ensureFieldExists(className: String, fieldName: String) { + if (classes.findField(className, fieldName) == null) { + throw UnknownApiException("Unknown field $className.$fieldName") + } + } + + private fun ensureMethodExists( + className: String, + methodName: String, + descriptor: String + ) { + if (classes.findMethod(className, methodName, descriptor) == null) { + throw UnknownApiException("Unknown method $className.$methodName$descriptor") + } + } + + fun setPolicyForClass(className: String, policy: FilterPolicyWithReason) { + ensureClassExists(className) + mPolicies[getClassKey(className)] = policy + } + + override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason { + return mPolicies[getFieldKey(className, fieldName)] + ?: super.getPolicyForField(className, fieldName) + } + + fun setPolicyForField(className: String, fieldName: String, policy: FilterPolicyWithReason) { + ensureFieldExists(className, fieldName) + mPolicies[getFieldKey(className, fieldName)] = policy + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String, + ): FilterPolicyWithReason { + return mPolicies[getMethodKey(className, methodName, descriptor)] + ?: super.getPolicyForMethod(className, methodName, descriptor) + } + + fun setPolicyForMethod( + className: String, + methodName: String, + descriptor: String, + policy: FilterPolicyWithReason, + ) { + ensureMethodExists(className, methodName, descriptor) + mPolicies[getMethodKey(className, methodName, descriptor)] = policy + } + + override fun getRenameTo(className: String, methodName: String, descriptor: String): String? { + return mRenames[getMethodKey(className, methodName, descriptor)] + ?: super.getRenameTo(className, methodName, descriptor) + } + + fun setRenameTo(className: String, methodName: String, descriptor: String, toName: String) { + ensureMethodExists(className, methodName, descriptor) + ensureMethodExists(className, toName, descriptor) + mRenames[getMethodKey(className, methodName, descriptor)] = toName + } + + override fun getNativeSubstitutionClass(className: String): String? { + return mNativeSubstitutionClasses[getClassKey(className)] + ?: super.getNativeSubstitutionClass(className) + } + + fun setNativeSubstitutionClass(from: String, to: String) { + ensureClassExists(from) + + // Native substitute classes may be provided from other jars, so we can't do this check. + // ensureClassExists(to) + mNativeSubstitutionClasses[getClassKey(from)] = to.toHumanReadableClassName() + } + + override fun getClassLoadHook(className: String): String? { + return mClassLoadHooks[getClassKey(className)] + ?: super.getClassLoadHook(className) + } + + fun setClassLoadHook(className: String, methodName: String) { + mClassLoadHooks[getClassKey(className)] = methodName.toHumanReadableMethodName() + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt new file mode 100644 index 000000000000..45dd38d10ef5 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +/** + * An [OutputFilter] that keeps all classes by default. (but none of its members) + * + * We're not currently using it, but using it *might* make certain things easier. For example, with + * this, all classes would at least be loadable. + */ +class KeepAllClassesFilter(fallback: OutputFilter) : DelegatingFilter(fallback) { + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + // If the default visibility wouldn't keep it, change it to "keep". + val f = super.getPolicyForClass(className) + return f.promoteToKeep("keep-all-classes") + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt new file mode 100644 index 000000000000..392ee4b81613 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +/** + * Base class for "filters", which decides what APIs should go to the stub / impl jars. + */ +abstract class OutputFilter { + /** + * Filters are stacked over one another. This fields contains the "outermost" filter in a + * filter stack chain. + * + * Subclasses must use this filter to get a policy, when they need to infer a policy + * from the policy of another API. + * + * For example, [ClassWidePolicyPropagatingFilter] needs to check the policy of the enclosing + * class to propagate "class-wide" policies, but when it does so, it can't just use + * `this.getPolicyForClass()` because that wouldn't return policies decided by "outer" + * filters. Instead, it uses [outermostFilter.getPolicyForClass()]. + * + * Note, [outermostFilter] can be itself, so make sure not to cause infinity recursions when + * using it. + */ + open var outermostFilter: OutputFilter = this + get() = field + set(value) { + field = value + } + + abstract fun getPolicyForClass(className: String): FilterPolicyWithReason + + abstract fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason + + abstract fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String, + ): FilterPolicyWithReason + + /** + * If a given method is a substitute-from method, return the substitute-to method name. + * + * The substitute-to and from methods must have the same signature, in the same class. + */ + open fun getRenameTo(className: String, methodName: String, descriptor: String): String? { + return null + } + + /** + * Return a "native substitution class" name for a given class. + * + * The result will be in a "human readable" form. (e.g. uses '.'s instead of '/'s) + * + * (which corresponds to @HostSideTestNativeSubstitutionClass of the standard annotations.) + */ + open fun getNativeSubstitutionClass(className: String): String? { + return null + } + + /** + * Return a "class load hook" method name for a given class. + * + * (which corresponds to @HostSideTestClassLoadHook of the standard annotations.) + */ + open fun getClassLoadHook(className: String): String? { + return null + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt new file mode 100644 index 000000000000..f92a0271d7c7 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.HostStubGenErrors +import com.android.hoststubgen.asm.ClassNodes + +private const val REASON = "demoted, not in intersect jars" + +/** + * An [OutputFilter] that will restrict what to put in stub to only what shows up in "intersecting + * jar" files. + * + * For example, if the Android public API stub jar is provided, then the HostStubGen's output + * stub will be restricted to public APIs. + */ +class StubIntersectingFilter( + private val errors: HostStubGenErrors, + /** + * If a class / field / method is not in any of these jars, then we will not put it in + * stub. + */ + private val intersectingJars: Map<String, ClassNodes>, + fallback: OutputFilter, +) : DelegatingFilter(fallback) { + private inline fun exists(predicate: (ClassNodes) -> Boolean): Boolean { + intersectingJars.forEach { entry -> + if (predicate(entry.value)) { + return true + } + } + return false + } + + /** + * If [origPolicy] is less than "Stub", then return it as-is. + * + * Otherwise, call [inStubChecker] to see if the API is in any of [intersectingJars]. + * If yes, then return [origPolicy] as-is. Otherwise, demote to "Keep". + */ + private fun intersectWithStub( + origPolicy: FilterPolicyWithReason, + inStubChecker: () -> Boolean, + ): FilterPolicyWithReason { + if (origPolicy.policy.needsInStub) { + // Only check the stub jars, when the class is supposed to be in stub otherwise. + if (!inStubChecker()) { + return origPolicy.demoteToKeep(REASON) + } + } + return origPolicy + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + return intersectWithStub(super.getPolicyForClass(className)) { + exists { classes -> classes.findClass(className) != null } + } + } + + override fun getPolicyForField( + className: String, + fieldName: String + ): FilterPolicyWithReason { + return intersectWithStub(super.getPolicyForField(className, fieldName)) { + exists { classes -> classes.findField(className, fieldName) != null } + } + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String + ): FilterPolicyWithReason { + return intersectWithStub(super.getPolicyForMethod(className, methodName, descriptor)) { + exists { classes -> classes.findMethod(className, methodName, descriptor) != null } + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt new file mode 100644 index 000000000000..46546e8b9491 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.UserErrorException +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.log +import com.android.hoststubgen.normalizeTextLine +import com.android.hoststubgen.whitespaceRegex +import org.objectweb.asm.Opcodes +import org.objectweb.asm.tree.ClassNode +import java.io.BufferedReader +import java.io.FileReader +import java.io.PrintWriter +import java.util.Objects + +/** + * Print a class node as a "keep" policy. + */ +fun printAsTextPolicy(pw: PrintWriter, cn: ClassNode) { + pw.printf("class %s\t%s\n", cn.name, "keep") + + for (f in cn.fields ?: emptyList()) { + pw.printf(" field %s\t%s\n", f.name, "keep") + } + for (m in cn.methods ?: emptyList()) { + pw.printf(" method %s\t%s\t%s\n", m.name, m.desc, "keep") + } +} + +/** Return true if [access] is either public or protected. */ +private fun isVisible(access: Int): Boolean { + return (access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PROTECTED)) != 0 +} + +/** + * Exception for a parse error. + */ +private class ParseException : Exception, UserErrorException { + val hasSourceInfo: Boolean + + constructor(message: String) : super(message) { + hasSourceInfo = false + } + + constructor(message: String, file: String, line: Int) : + super("$message in file $file line $line") { + hasSourceInfo = true + } + + fun withSourceInfo(filename: String, lineNo: Int): ParseException { + if (hasSourceInfo) { + return this // Already has source information. + } else { + return ParseException(this.message ?: "", filename, lineNo) + } + } +} + +private const val FILTER_REASON = "file-override" + +/** + * Read a given "policy" file and return as an [OutputFilter] + */ +fun createFilterFromTextPolicyFile( + filename: String, + classes: ClassNodes, + fallback: OutputFilter, + ): OutputFilter { + log.i("Loading offloaded annotations from $filename ...") + log.withIndent { + val ret = InMemoryOutputFilter(classes, fallback) + + var lineNo = 0 + + try { + BufferedReader(FileReader(filename)).use { reader -> + var className = "" + + while (true) { + var line = reader.readLine() + if (line == null) { + break + } + lineNo++ + + line = normalizeTextLine(line) + + if (line.isEmpty()) { + continue // skip empty lines. + } + + val fields = line.split(whitespaceRegex).toTypedArray() + when (fields[0].lowercase()) { + "c", "class" -> { + if (fields.size < 3) { + throw ParseException("Class ('c') expects 2 fields.") + } + className = fields[1] + if (fields[2].startsWith("!")) { + // It's a native-substitution. + val toClass = fields[2].substring(1) + ret.setNativeSubstitutionClass(className, toClass) + } else if (fields[2].startsWith("~")) { + // It's a class-load hook + val callback = fields[2].substring(1) + ret.setClassLoadHook(className, callback) + } else { + val policy = parsePolicy(fields[2]) + if (!policy.isUsableWithClasses) { + throw ParseException("Class can't have policy '$policy'") + } + Objects.requireNonNull(className) + + // TODO: Duplicate check, etc + ret.setPolicyForClass(className, policy.withReason(FILTER_REASON)) + } + } + + "f", "field" -> { + if (fields.size < 3) { + throw ParseException("Field ('f') expects 2 fields.") + } + val name = fields[1] + val policy = parsePolicy(fields[2]) + if (!policy.isUsableWithFields) { + throw ParseException("Field can't have policy '$policy'") + } + Objects.requireNonNull(className) + + // TODO: Duplicate check, etc + ret.setPolicyForField(className, name, policy.withReason(FILTER_REASON)) + } + + "m", "method" -> { + if (fields.size < 4) { + throw ParseException("Method ('m') expects 3 fields.") + } + val name = fields[1] + val signature = fields[2] + val policy = parsePolicy(fields[3]) + + if (!policy.isUsableWithMethods) { + throw ParseException("Method can't have policy '$policy'") + } + + Objects.requireNonNull(className) + + ret.setPolicyForMethod(className, name, signature, + policy.withReason(FILTER_REASON)) + if (policy.isSubstitute) { + val fromName = fields[3].substring(1) + + if (fromName == name) { + throw ParseException( + "Substitution must have a different name") + } + + // Set the policy for the "from" method. + ret.setPolicyForMethod(className, fromName, signature, + policy.getSubstitutionBasePolicy() + .withReason(FILTER_REASON)) + + // Keep "from" -> "to" mapping. + ret.setRenameTo(className, fromName, signature, name) + } + } + + else -> { + throw ParseException("Unknown directive \"${fields[0]}\"") + } + } + } + } + } catch (e: ParseException) { + throw e.withSourceInfo(filename, lineNo) + } + return ret + } +} + +private fun parsePolicy(s: String): FilterPolicy { + return when (s.lowercase()) { + "s", "stub" -> FilterPolicy.Stub + "k", "keep" -> FilterPolicy.Keep + "t", "throw" -> FilterPolicy.Throw + "r", "remove" -> FilterPolicy.Remove + "sc", "stubclass" -> FilterPolicy.StubClass + "kc", "keepclass" -> FilterPolicy.KeepClass + else -> { + if (s.startsWith("@")) { + FilterPolicy.SubstituteAndStub + } else if (s.startsWith("%")) { + FilterPolicy.SubstituteAndKeep + } else { + throw ParseException("Invalid policy \"$s\"") + } + } + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt new file mode 100644 index 000000000000..3cf9a1d7fb82 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.visitors + +import com.android.hoststubgen.HostStubGenErrors +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.getPackageNameFromClassName +import com.android.hoststubgen.asm.resolveClassName +import com.android.hoststubgen.asm.toJvmClassName +import com.android.hoststubgen.filters.FilterPolicy +import com.android.hoststubgen.filters.FilterPolicyWithReason +import com.android.hoststubgen.filters.OutputFilter +import com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +import com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass +import com.android.hoststubgen.log +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.FieldVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.util.TraceClassVisitor +import java.io.PrintWriter + +val OPCODE_VERSION = Opcodes.ASM9 + +abstract class BaseAdapter ( + protected val classes: ClassNodes, + nextVisitor: ClassVisitor, + protected val filter: OutputFilter, + protected val options: Options, +) : ClassVisitor(OPCODE_VERSION, nextVisitor) { + + /** + * Options to control the behavior. + */ + data class Options ( + val errors: HostStubGenErrors, + val enablePreTrace: Boolean, + val enablePostTrace: Boolean, + val enableMethodLogging: Boolean, + val enableNonStubMethodCallDetection: Boolean, + ) + + protected lateinit var currentPackageName: String + protected lateinit var currentClassName: String + protected var nativeSubstitutionClass: String? = null + protected lateinit var classPolicy: FilterPolicyWithReason + + /** + * Return whether an item with a given policy should be included in the output. + */ + protected abstract fun shouldEmit(policy: FilterPolicy): Boolean + + override fun visit( + version: Int, + access: Int, + name: String, + signature: String?, + superName: String?, + interfaces: Array<String>, + ) { + super.visit(version, access, name, signature, superName, interfaces) + currentClassName = name + currentPackageName = getPackageNameFromClassName(name) + classPolicy = filter.getPolicyForClass(currentClassName) + + log.d("[%s] visit: %s (package: %s)", this.javaClass.simpleName, name, currentPackageName) + log.indent() + log.v("Emitting class: %s", name) + log.indent() + + filter.getNativeSubstitutionClass(currentClassName)?.let { className -> + val fullClassName = resolveClassName(className, currentPackageName).toJvmClassName() + log.d(" NativeSubstitutionClass: $fullClassName") + if (classes.findClass(fullClassName) == null) { + log.w("Native substitution class $fullClassName not found. Class must be " + + "available at runtime.") + } else { + // If the class exists, it must have a KeepClass policy. + if (filter.getPolicyForClass(fullClassName).policy != FilterPolicy.KeepClass) { + // TODO: Use real annotation name. + options.errors.onErrorFound( + "Native substitution class $fullClassName should have @Keep.") + } + } + + nativeSubstitutionClass = fullClassName + } + // Inject annotations to generated classes. + if (classPolicy.policy.needsInStub) { + visitAnnotation(HostStubGenProcessedStubClass.CLASS_DESCRIPTOR, true) + } + if (classPolicy.policy.needsInImpl) { + visitAnnotation(HostStubGenProcessedKeepClass.CLASS_DESCRIPTOR, true) + } + } + + override fun visitEnd() { + log.unindent() + log.unindent() + super.visitEnd() + } + + var skipMemberModificationNestCount = 0 + + /** + * This method allows writing class members without any modifications. + */ + protected inline fun writeRawMembers(callback: () -> Unit) { + skipMemberModificationNestCount++ + try { + callback() + } finally { + skipMemberModificationNestCount-- + } + } + + override fun visitField( + access: Int, + name: String, + descriptor: String, + signature: String?, + value: Any?, + ): FieldVisitor? { + if (skipMemberModificationNestCount > 0) { + return super.visitField(access, name, descriptor, signature, value) + } + val policy = filter.getPolicyForField(currentClassName, name) + log.d("visitField: %s %s [%x] Policy: %s", name, descriptor, access, policy) + + log.withIndent { + if (!shouldEmit(policy.policy)) { + log.d("Removing %s %s", name, policy) + return null + } + + log.v("Emitting field: %s %s %s", name, descriptor, policy) + return super.visitField(access, name, descriptor, signature, value) + } + } + + override fun visitMethod( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + ): MethodVisitor? { + if (skipMemberModificationNestCount > 0) { + return super.visitMethod(access, name, descriptor, signature, exceptions) + } + val p = filter.getPolicyForMethod(currentClassName, name, descriptor) + log.d("visitMethod: %s%s [%x] [%s] Policy: %s", name, descriptor, access, signature, p) + + log.withIndent { + // If it's a substitute-to method, then skip. + val policy = filter.getPolicyForMethod(currentClassName, name, descriptor) + if (policy.policy.isSubstitute) { + log.d("Skipping %s%s %s", name, descriptor, policy) + return null + } + if (!shouldEmit(p.policy)) { + log.d("Removing %s%s %s", name, descriptor, policy) + return null + } + + // Maybe rename the method. + val newName: String + val substituteTo = filter.getRenameTo(currentClassName, name, descriptor) + if (substituteTo != null) { + newName = substituteTo + log.v("Emitting %s.%s%s as %s %s", currentClassName, name, descriptor, + newName, policy) + } else { + log.v("Emitting method: %s%s %s", name, descriptor, policy) + newName = name + } + + // Let subclass update the flag. + // But note, we only use it when calling the super's method, + // but not for visitMethodInner(), beucase when subclass wants to change access, + // it can do so inside visitMethodInner(). + val newAccess = updateAccessFlags(access, name, descriptor) + + return visitMethodInner(access, newName, descriptor, signature, exceptions, policy, + super.visitMethod(newAccess, newName, descriptor, signature, exceptions)) + } + } + + open fun updateAccessFlags( + access: Int, + name: String, + descriptor: String, + ): Int { + return access + } + + abstract fun visitMethodInner( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + policy: FilterPolicyWithReason, + superVisitor: MethodVisitor?, + ): MethodVisitor? + + companion object { + fun getVisitor( + classes: ClassNodes, + nextVisitor: ClassVisitor, + filter: OutputFilter, + forImpl: Boolean, + options: Options, + ): ClassVisitor { + var next = nextVisitor + + val verbosePrinter = PrintWriter(log.getVerbosePrintStream()) + + // TODO: This doesn't work yet. + + // Inject TraceClassVisitor for debugging. + if (options.enablePostTrace) { + next = TraceClassVisitor(next, verbosePrinter) + } + var ret: ClassVisitor + if (forImpl) { + ret = ImplGeneratingAdapter(classes, next, filter, options) + } else { + ret = StubGeneratingAdapter(classes, next, filter, options) + } + + // Inject TraceClassVisitor for debugging. + if (options.enablePreTrace) { + ret = TraceClassVisitor(ret, verbosePrinter) + } + return ret + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt new file mode 100644 index 000000000000..8250412b3717 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.visitors + +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.Attribute +import org.objectweb.asm.Handle +import org.objectweb.asm.Label +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.TypePath + +/** + * A method visitor that removes everything from method body. + * + * To inject a method body, override [visitCode] and create the opcodes there. + */ +abstract class BodyReplacingMethodVisitor( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor?, +) : MethodVisitor(OPCODE_VERSION, next) { + val isVoid: Boolean + val isStatic: Boolean + + init { + isVoid = descriptor.endsWith(")V") + isStatic = access and Opcodes.ACC_STATIC != 0 + } + + // Following methods are for things that we need to keep. + // Since they're all calling the super method, we can just remove them, but we keep them + // just to clarify what we're keeping. + + final override fun visitParameter( + name: String?, + access: Int + ) { + super.visitParameter(name, access) + } + + final override fun visitAnnotationDefault(): AnnotationVisitor? { + return super.visitAnnotationDefault() + } + + final override fun visitAnnotation( + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + return super.visitAnnotation(descriptor, visible) + } + + final override fun visitTypeAnnotation( + typeRef: Int, + typePath: TypePath?, + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible) + } + + final override fun visitAnnotableParameterCount( + parameterCount: Int, + visible: Boolean + ) { + super.visitAnnotableParameterCount(parameterCount, visible) + } + + final override fun visitParameterAnnotation( + parameter: Int, + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + return super.visitParameterAnnotation(parameter, descriptor, visible) + } + + final override fun visitAttribute(attribute: Attribute?) { + super.visitAttribute(attribute) + } + + override fun visitEnd() { + super.visitEnd() + } + + /** + * Control when to emit the code. We use this to ignore all visitXxx method calls caused by + * the original method, so we'll remove all the original code. + * + * Only when visitXxx methods are called from [emitNewCode], we pass-through to the base class, + * so the body will be generated. + * + * (See also https://asm.ow2.io/asm4-guide.pdf section 3.2.1 about the MethovVisitor + * call order.) + */ + var emitCode = false + + final override fun visitCode() { + super.visitCode() + + try { + emitCode = true + + emitNewCode() + } finally { + emitCode = false + } + } + + /** + * Subclass must implement it and emit code, and call [visitMaxs] at the end. + */ + abstract fun emitNewCode() + + final override fun visitMaxs( + maxStack: Int, + maxLocals: Int + ) { + if (emitCode) { + super.visitMaxs(maxStack, maxLocals) + } + } + + // Following methods are called inside a method body, and we don't want to + // emit any of them, so they are all no-op. + + final override fun visitFrame( + type: Int, + numLocal: Int, + local: Array<out Any>?, + numStack: Int, + stack: Array<out Any>? + ) { + if (emitCode) { + super.visitFrame(type, numLocal, local, numStack, stack) + } + } + + final override fun visitInsn(opcode: Int) { + if (emitCode) { + super.visitInsn(opcode) + } + } + + final override fun visitIntInsn( + opcode: Int, + operand: Int + ) { + if (emitCode) { + super.visitIntInsn(opcode, operand) + } + } + + final override fun visitVarInsn( + opcode: Int, + varIndex: Int + ) { + if (emitCode) { + super.visitVarInsn(opcode, varIndex) + } + } + + final override fun visitTypeInsn( + opcode: Int, + type: String? + ) { + if (emitCode) { + super.visitTypeInsn(opcode, type) + } + } + + final override fun visitFieldInsn( + opcode: Int, + owner: String?, + name: String?, + descriptor: String? + ) { + if (emitCode) { + super.visitFieldInsn(opcode, owner, name, descriptor) + } + } + + final override fun visitMethodInsn( + opcode: Int, + owner: String?, + name: String?, + descriptor: String?, + isInterface: Boolean + ) { + if (emitCode) { + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) + } + } + + final override fun visitInvokeDynamicInsn( + name: String?, + descriptor: String?, + bootstrapMethodHandle: Handle?, + vararg bootstrapMethodArguments: Any? + ) { + if (emitCode) { + super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, + *bootstrapMethodArguments) + } + } + + final override fun visitJumpInsn( + opcode: Int, + label: Label? + ) { + if (emitCode) { + super.visitJumpInsn(opcode, label) + } + } + + final override fun visitLabel(label: Label?) { + if (emitCode) { + super.visitLabel(label) + } + } + + final override fun visitLdcInsn(value: Any?) { + if (emitCode) { + super.visitLdcInsn(value) + } + } + + final override fun visitIincInsn( + varIndex: Int, + increment: Int + ) { + if (emitCode) { + super.visitIincInsn(varIndex, increment) + } + } + + final override fun visitTableSwitchInsn( + min: Int, + max: Int, + dflt: Label?, + vararg labels: Label? + ) { + if (emitCode) { + super.visitTableSwitchInsn(min, max, dflt, *labels) + } + } + + final override fun visitLookupSwitchInsn( + dflt: Label?, + keys: IntArray?, + labels: Array<out Label>? + ) { + if (emitCode) { + super.visitLookupSwitchInsn(dflt, keys, labels) + } + } + + final override fun visitMultiANewArrayInsn( + descriptor: String?, + numDimensions: Int + ) { + if (emitCode) { + super.visitMultiANewArrayInsn(descriptor, numDimensions) + } + } + + final override fun visitInsnAnnotation( + typeRef: Int, + typePath: TypePath?, + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + if (emitCode) { + return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible) + } + return null + } + + final override fun visitTryCatchBlock( + start: Label?, + end: Label?, + handler: Label?, + type: String? + ) { + if (emitCode) { + super.visitTryCatchBlock(start, end, handler, type) + } + } + + final override fun visitTryCatchAnnotation( + typeRef: Int, + typePath: TypePath?, + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + if (emitCode) { + return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible) + } + return null + } + + final override fun visitLocalVariable( + name: String?, + descriptor: String?, + signature: String?, + start: Label?, + end: Label?, + index: Int + ) { + if (emitCode) { + super.visitLocalVariable(name, descriptor, signature, start, end, index) + } + } + + final override fun visitLocalVariableAnnotation( + typeRef: Int, + typePath: TypePath?, + start: Array<out Label>?, + end: Array<out Label>?, + index: IntArray?, + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + if (emitCode) { + return super.visitLocalVariableAnnotation( + typeRef, typePath, start, end, index, descriptor, visible) + } + return null + } + + final override fun visitLineNumber( + line: Int, + start: Label? + ) { + if (emitCode) { + super.visitLineNumber(line, start) + } + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt new file mode 100644 index 000000000000..ac068861ec8d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.visitors + +import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC +import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate +import com.android.hoststubgen.asm.writeByteCodeToPushArguments +import com.android.hoststubgen.asm.writeByteCodeToReturn +import com.android.hoststubgen.filters.FilterPolicy +import com.android.hoststubgen.filters.FilterPolicyWithReason +import com.android.hoststubgen.filters.OutputFilter +import com.android.hoststubgen.hosthelper.HostTestUtils +import com.android.hoststubgen.log +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type + +/** + * An adapter that generates the "impl" class file from an input class file. + */ +class ImplGeneratingAdapter( + classes: ClassNodes, + nextVisitor: ClassVisitor, + filter: OutputFilter, + options: Options, +) : BaseAdapter(classes, nextVisitor, filter, options) { + + override fun shouldEmit(policy: FilterPolicy): Boolean { + return policy.needsInImpl + } + + private var classLoadHookMethod: String? = null + + override fun visit( + version: Int, + access: Int, + name: String, + signature: String?, + superName: String?, + interfaces: Array<String> + ) { + super.visit(version, access, name, signature, superName, interfaces) + + classLoadHookMethod = filter.getClassLoadHook(currentClassName) + + // classLoadHookMethod is non-null, then we need to inject code to call it + // in the class initializer. + // If the target class already has a class initializer, then we need to inject code to it. + // Otherwise, we need to create one. + + classLoadHookMethod?.let { callback -> + log.d(" ClassLoadHook: $callback") + if (!classes.hasClassInitializer(currentClassName)) { + injectClassLoadHook(callback) + } + } + } + + private fun injectClassLoadHook(callback: String) { + writeRawMembers { + // Create a class initializer to call onClassLoaded(). + // Each class can only have at most one class initializer, but the base class + // StaticInitMerger will merge it with the existing one, if any. + visitMethod( + Opcodes.ACC_PRIVATE or Opcodes.ACC_STATIC, + "<clinit>", + "()V", + null, + null + )!!.let { mv -> + // Method prologue + mv.visitCode() + + writeClassLoadHookCall(mv) + mv.visitInsn(Opcodes.RETURN) + + // Method epilogue + mv.visitMaxs(0, 0) + mv.visitEnd() + } + } + } + + private fun writeClassLoadHookCall(mv: MethodVisitor) { + // First argument: the class type. + mv.visitLdcInsn(Type.getType("L" + currentClassName + ";")) + + // Second argument: method name + mv.visitLdcInsn(classLoadHookMethod) + + // Call HostTestUtils.onClassLoaded(). + mv.visitMethodInsn( + Opcodes.INVOKESTATIC, + HostTestUtils.CLASS_INTERNAL_NAME, + "onClassLoaded", + "(Ljava/lang/Class;Ljava/lang/String;)V", + false + ) + } + + override fun updateAccessFlags( + access: Int, + name: String, + descriptor: String, + ): Int { + if ((access and Opcodes.ACC_NATIVE) != 0 && nativeSubstitutionClass != null) { + return access and Opcodes.ACC_NATIVE.inv() + } + return access + } + + override fun visitMethodInner( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + policy: FilterPolicyWithReason, + superVisitor: MethodVisitor?, + ): MethodVisitor? { + // Inject method log, if needed. + var innerVisitor = superVisitor + + // If method logging is enabled, inject call to the logging method. + if (options.enableMethodLogging) { + innerVisitor = LogInjectingMethodAdapter( + access, + name, + descriptor, + signature, + exceptions, + innerVisitor, + ) + } + + // If this class already has a class initializer and a class load hook is needed, then + // we inject code. + if (classLoadHookMethod != null && + name == CLASS_INITIALIZER_NAME && + descriptor == CLASS_INITIALIZER_DESC) { + innerVisitor = ClassLoadHookInjectingMethodAdapter( + access, + name, + descriptor, + signature, + exceptions, + innerVisitor, + ) + } + + // If non-stub method call detection is enabled, then inject a call to the checker. + if (options.enableNonStubMethodCallDetection && doesMethodNeedNonStubCallCheck( + access, name, descriptor, policy) ) { + innerVisitor = NonStubMethodCallDetectingAdapter( + access, + name, + descriptor, + signature, + exceptions, + innerVisitor, + ) + } + + log.withIndent { + if ((access and Opcodes.ACC_NATIVE) != 0 && nativeSubstitutionClass != null) { + log.v("Rewriting native method...") + return NativeSubstitutingMethodAdapter( + access, name, descriptor, signature, exceptions, innerVisitor) + } + if (policy.policy == FilterPolicy.Throw) { + log.v("Making method throw...") + return ThrowingMethodAdapter( + access, name, descriptor, signature, exceptions, innerVisitor) + } + } + + return innerVisitor + } + + fun doesMethodNeedNonStubCallCheck( + access: Int, + name: String, + descriptor: String, + policy: FilterPolicyWithReason, + ): Boolean { + // If a method is in the stub, then no need to check. + if (policy.policy.needsInStub) { + return false + } + // If a method is private or package-private, no need to check. + // Technically test code can use framework package name, so it's a bit too lenient. + if (isVisibilityPrivateOrPackagePrivate(access)) { + return false + } + // TODO: If the method overrides a method that's accessible by tests, then we shouldn't + // do the check. (e.g. overrides a stub method or java standard method.) + + return true + } + + /** + * A method adapter that replaces the method body with a HostTestUtils.onThrowMethodCalled() + * call. + */ + private inner class ThrowingMethodAdapter( + access: Int, + val name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) { + override fun emitNewCode() { + visitMethodInsn(Opcodes.INVOKESTATIC, + HostTestUtils.CLASS_INTERNAL_NAME, + "onThrowMethodCalled", + "()V", + false) + + // We still need a RETURN opcode for the return type. + // For now, let's just inject a `throw`. + visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException") + visitInsn(Opcodes.DUP) + visitLdcInsn("Unreachable") + visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException", + "<init>", "(Ljava/lang/String;)V", false) + visitInsn(Opcodes.ATHROW) + + // visitMaxs(3, if (isStatic) 0 else 1) + visitMaxs(0, 0) // We let ASM figure them out. + } + } + + /** + * A method adapter that replaces a native method call with a call to the "native substitution" + * class. + */ + private inner class NativeSubstitutingMethodAdapter( + access: Int, + private val name: String, + private val descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : MethodVisitor(OPCODE_VERSION, next) { + override fun visitCode() { + super.visitCode() + + throw RuntimeException("NativeSubstitutingMethodVisitor should be called on " + + " native method, where visitCode() shouldn't be called.") + } + + override fun visitEnd() { + writeByteCodeToPushArguments(descriptor, this) + + visitMethodInsn(Opcodes.INVOKESTATIC, + nativeSubstitutionClass, + name, + descriptor, + false) + + writeByteCodeToReturn(descriptor, this) + + visitMaxs(99, 0) // We let ASM figure them out. + super.visitEnd() + } + } + + /** + * A method adapter that injects a call to HostTestUtils.logMethodCall() to every method. + * + * Note, when the target method is a constructor, it may contain calls to `super(...)` or + * `this(...)`. The logging code will be injected *before* such calls. + */ + private inner class LogInjectingMethodAdapter( + access: Int, + val name: String, + val descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : MethodVisitor(OPCODE_VERSION, next) { + override fun visitCode() { + super.visitCode() + visitLdcInsn(currentClassName) + visitLdcInsn(name) + visitLdcInsn(descriptor) + visitMethodInsn(Opcodes.INVOKESTATIC, + HostTestUtils.CLASS_INTERNAL_NAME, + "logMethodCall", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + false) + } + } + + /** + * Inject a class load hook call. + */ + private inner class ClassLoadHookInjectingMethodAdapter( + access: Int, + val name: String, + val descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : MethodVisitor(OPCODE_VERSION, next) { + override fun visitCode() { + super.visitCode() + + writeClassLoadHookCall(this) + } + } + + /** + * A method adapter that detects calls to non-stub methods. + */ + private inner class NonStubMethodCallDetectingAdapter( + access: Int, + val name: String, + val descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : MethodVisitor(OPCODE_VERSION, next) { + override fun visitCode() { + super.visitCode() + + // First three arguments to HostTestUtils.onNonStubMethodCalled(). + visitLdcInsn(currentClassName) + visitLdcInsn(name) + visitLdcInsn(descriptor) + + // Call: HostTestUtils.getStackWalker().getCallerClass(). + // This push the caller Class in the stack. + visitMethodInsn(Opcodes.INVOKESTATIC, + HostTestUtils.CLASS_INTERNAL_NAME, + "getStackWalker", + "()Ljava/lang/StackWalker;", + false) + visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/lang/StackWalker", + "getCallerClass", + "()Ljava/lang/Class;", + false) + + // Then call onNonStubMethodCalled(). + visitMethodInsn(Opcodes.INVOKESTATIC, + HostTestUtils.CLASS_INTERNAL_NAME, + "onNonStubMethodCalled", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V", + false) + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt new file mode 100644 index 000000000000..37e2a888969d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.visitors + +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.filters.FilterPolicy +import com.android.hoststubgen.filters.FilterPolicyWithReason +import com.android.hoststubgen.filters.OutputFilter +import com.android.hoststubgen.log +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes + +/** + * An adapter that generates the "impl" class file from an input class file. + */ +class StubGeneratingAdapter( + classes: ClassNodes, + nextVisitor: ClassVisitor, + filter: OutputFilter, + options: Options, +) : BaseAdapter(classes, nextVisitor, filter, options) { + + override fun shouldEmit(policy: FilterPolicy): Boolean { + return policy.needsInStub + } + + override fun visitMethodInner( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + policy: FilterPolicyWithReason, + superVisitor: MethodVisitor?, + ): MethodVisitor? { + return StubMethodVisitor(access, name, descriptor, signature, exceptions, superVisitor) + } + + private inner class StubMethodVisitor( + access: Int, + val name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) { + override fun emitNewCode() { + log.d(" Generating stub method for $currentClassName.$name") + + // Inject the following code: + // throw new RuntimeException("Stub!"); + + /* + NEW java/lang/RuntimeException + DUP + LDC "not supported on host side" + INVOKESPECIAL java/lang/RuntimeException.<init> (Ljava/lang/String;)V + ATHROW + MAXSTACK = 3 + MAXLOCALS = 2 <- 1 for this, 1 for return value. + */ + visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException") + visitInsn(Opcodes.DUP) + visitLdcInsn("Stub!") + visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException", + "<init>", "(Ljava/lang/String;)V", false) + visitInsn(Opcodes.ATHROW) + visitMaxs(0, 0) // We let ASM figure them out. + } + } +} diff --git a/tools/hoststubgen/hoststubgen/test-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-framework/Android.bp new file mode 100644 index 000000000000..2b91cc161b7f --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/Android.bp @@ -0,0 +1,19 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +build = ["AndroidHostTest.bp"] diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp new file mode 100644 index 000000000000..e7fb2debfc4e --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Add `build = ["AndroidHostTest.bp"]` to Android.bp to include this file. + +// Compile the test jar, using 2 rules. +// 1. Build the test against the stub. +java_library_host { + name: "HostStubGenTest-framework-test-host-test-lib", + defaults: ["hosttest-with-framework-all-hidden-api-test-lib-defaults"], + srcs: [ + "src/**/*.java", + ], + static_libs: [ + "junit", + "truth-prebuilt", + "mockito", + + // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/ + "platform-test-annotations", + "hoststubgen-annotations", + ], +} + +// 2. Link the above module with necessary runtime dependencies, so it can be executed stand-alone. +java_test_host { + name: "HostStubGenTest-framework-all-test-host-test", + defaults: ["hosttest-with-framework-all-hidden-api-test-defaults"], + static_libs: [ + "HostStubGenTest-framework-test-host-test-lib", + ], + test_suites: ["general-tests"], +} diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml b/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml new file mode 100644 index 000000000000..f35dcf69ecc9 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- [Ravenwood] Copied from $ANDROID_BUILD_TOP/cts/hostsidetests/devicepolicy/AndroidTest.xml --> +<configuration description="CtsContentTestCases host-side test"> + <option name="test-suite-tag" value="ravenwood" /> + <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" /> + <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> + + <test class="com.android.tradefed.testtype.IsolatedHostTest" > + <option name="jar" value="HostStubGenTest-framework-all-test-host-test.jar" /> + </test> +</configuration> diff --git a/tools/hoststubgen/hoststubgen/test-framework/README.md b/tools/hoststubgen/hoststubgen/test-framework/README.md new file mode 100644 index 000000000000..20e2f873b152 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/README.md @@ -0,0 +1,27 @@ +# HostStubGen: real framework test + +This directory contains tests against the actual framework.jar code. The tests were +copied from somewhere else in the android tree. We use this directory to quickly run existing +tests. + +## How to run + +- With `atest`. This is the proper way to run it, but it may fail due to atest's known problems. + + See the top level README.md on why `--no-bazel-mode` is needed (for now). + +``` +$ atest --no-bazel-mode HostStubGenTest-framework-test-host-test +``` + +- With `run-ravenwood-test` + +``` +$ run-ravenwood-test HostStubGenTest-framework-test-host-test +``` + +- Advanced option: `run-test-without-atest.sh` runs the test without using `atest` or `run-ravenwood-test` + +``` +$ ./run-test-without-atest.sh +```
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh b/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh new file mode 100755 index 000000000000..cfc06a12e881 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh @@ -0,0 +1,85 @@ +#!/bin/bash +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Run HostStubGenTest-framework-test-host-test directly with JUnit. +# (without using atest.) + +source "${0%/*}"/../../common.sh + + +# Options: +# -v enable verbose log +# -d enable debugger + +verbose=0 +debug=0 +while getopts "vd" opt; do + case "$opt" in + v) verbose=1 ;; + d) debug=1 ;; + esac +done +shift $(($OPTIND - 1)) + + +if (( $verbose )) ; then + JAVA_OPTS="$JAVA_OPTS -verbose:class" +fi + +if (( $debug )) ; then + JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8700" +fi + +#======================================= +module=HostStubGenTest-framework-all-test-host-test +module_jar=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/$module/$module.jar +run m $module + +out=out + +rm -fr $out +mkdir -p $out + + +# Copy and extract the relevant jar files so we can look into them. +run cp \ + $module_jar \ + $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/framework-all-hidden-api-host/linux_glibc_common/gen/*.jar \ + $out + +run extract $out/*.jar + +# Result is the number of failed tests. +result=0 + + +# This suite runs all tests in the JAR. +tests=(com.android.hoststubgen.hosthelper.HostTestSuite) + +# Uncomment this to run a specific test. +# tests=(com.android.hoststubgen.frameworktest.LogTest) + + +for class in ${tests[@]} ; do + echo "Running $class ..." + + run cd "${module_jar%/*}" + run $JAVA $JAVA_OPTS \ + -cp $module_jar \ + org.junit.runner.JUnitCore \ + $class || result=$(( $result + 1 )) +done + +exit $result diff --git a/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java new file mode 100644 index 000000000000..62bbf48955da --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java @@ -0,0 +1,737 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.frameworktest; + +// [ravewnwood] Copied from cts/, and commented out unsupported stuff. + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; +import android.util.Log; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.function.BiFunction; + +/** + * Some basic tests for {@link android.util.ArrayMap}. + */ +@RunWith(AndroidJUnit4.class) +public class ArrayMapTest { + static final boolean DEBUG = false; + + static final int OP_ADD = 1; + static final int OP_REM = 2; + + static int[] OPS = new int[]{ + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_ADD, OP_ADD, OP_ADD, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + OP_REM, OP_REM, OP_REM, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + }; + + static int[] KEYS = new int[]{ + // General adding and removing. + -1, 1900, 600, 200, 1200, 1500, 1800, 100, 1900, + 2100, 300, 800, 600, 1100, 1300, 2000, 1000, 1400, + 600, -1, 1900, 600, 300, 2100, 200, 800, 800, + 1800, 1500, 1300, 1100, 2000, 1400, 1000, 1200, 1900, + + // Shrink when removing item from end. + 100, 200, 300, 400, 500, 600, 700, 800, 900, + 900, 800, 700, 600, 500, 400, 300, 200, 100, + + // Shrink when removing item from middle. + 100, 200, 300, 400, 500, 600, 700, 800, 900, + 900, 800, 700, 600, 500, 400, 200, 300, 100, + + // Shrink when removing item from front. + 100, 200, 300, 400, 500, 600, 700, 800, 900, + 900, 800, 700, 600, 500, 400, 100, 200, 300, + + // Test hash collisions. + 105, 106, 108, 104, 102, 102, 107, 5, 205, + 4, 202, 203, 3, 5, 101, 109, 200, 201, + 0, -1, 100, + 106, 108, 104, 102, 103, 105, 107, 101, 109, + -1, 100, 0, + 4, 5, 3, 5, 200, 203, 202, 201, 205, + }; + + public static class ControlledHash implements Parcelable { + final int mValue; + + ControlledHash(int value) { + mValue = value; + } + + @Override + public final boolean equals(Object o) { + if (o == null) { + return false; + } + return mValue == ((ControlledHash)o).mValue; + } + + @Override + public final int hashCode() { + return mValue/100; + } + + @Override + public final String toString() { + return Integer.toString(mValue); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mValue); + } + + public static final Parcelable.Creator<ControlledHash> CREATOR + = new Parcelable.Creator<ControlledHash>() { + public ControlledHash createFromParcel(Parcel in) { + return new ControlledHash(in.readInt()); + } + + public ControlledHash[] newArray(int size) { + return new ControlledHash[size]; + } + }; + } + + private static boolean compare(Object v1, Object v2) { + if (v1 == null) { + return v2 == null; + } + if (v2 == null) { + return false; + } + return v1.equals(v2); + } + + private static void compareMaps(HashMap map, ArrayMap array) { + if (map.size() != array.size()) { + fail("Bad size: expected " + map.size() + ", got " + array.size()); + } + + Set<Entry> mapSet = map.entrySet(); + for (Map.Entry entry : mapSet) { + Object expValue = entry.getValue(); + Object gotValue = array.get(entry.getKey()); + if (!compare(expValue, gotValue)) { + fail("Bad value: expected " + expValue + ", got " + gotValue + + " at key " + entry.getKey()); + } + } + + for (int i = 0; i < array.size(); i++) { + Object gotValue = array.valueAt(i); + Object key = array.keyAt(i); + Object expValue = map.get(key); + if (!compare(expValue, gotValue)) { + fail("Bad value: expected " + expValue + ", got " + gotValue + + " at key " + key); + } + } + + if (map.entrySet().hashCode() != array.entrySet().hashCode()) { + fail("Entry set hash codes differ: map=0x" + + Integer.toHexString(map.entrySet().hashCode()) + " array=0x" + + Integer.toHexString(array.entrySet().hashCode())); + } + + if (!map.entrySet().equals(array.entrySet())) { + fail("Failed calling equals on map entry set against array set"); + } + + if (!array.entrySet().equals(map.entrySet())) { + fail("Failed calling equals on array entry set against map set"); + } + + if (map.keySet().hashCode() != array.keySet().hashCode()) { + fail("Key set hash codes differ: map=0x" + + Integer.toHexString(map.keySet().hashCode()) + " array=0x" + + Integer.toHexString(array.keySet().hashCode())); + } + + if (!map.keySet().equals(array.keySet())) { + fail("Failed calling equals on map key set against array set"); + } + + if (!array.keySet().equals(map.keySet())) { + fail("Failed calling equals on array key set against map set"); + } + + if (!map.keySet().containsAll(array.keySet())) { + fail("Failed map key set contains all of array key set"); + } + + if (!array.keySet().containsAll(map.keySet())) { + fail("Failed array key set contains all of map key set"); + } + + if (!array.containsAll(map.keySet())) { + fail("Failed array contains all of map key set"); + } + + if (!map.entrySet().containsAll(array.entrySet())) { + fail("Failed map entry set contains all of array entry set"); + } + + if (!array.entrySet().containsAll(map.entrySet())) { + fail("Failed array entry set contains all of map entry set"); + } + } + + private static void validateArrayMap(ArrayMap array) { + Set<Map.Entry> entrySet = array.entrySet(); + int index = 0; + Iterator<Entry> entryIt = entrySet.iterator(); + while (entryIt.hasNext()) { + Map.Entry entry = entryIt.next(); + Object value = entry.getKey(); + Object realValue = array.keyAt(index); + if (!compare(realValue, value)) { + fail("Bad array map entry set: expected key " + realValue + + ", got " + value + " at index " + index); + } + value = entry.getValue(); + realValue = array.valueAt(index); + if (!compare(realValue, value)) { + fail("Bad array map entry set: expected value " + realValue + + ", got " + value + " at index " + index); + } + index++; + } + + index = 0; + Set keySet = array.keySet(); + Iterator keyIt = keySet.iterator(); + while (keyIt.hasNext()) { + Object value = keyIt.next(); + Object realValue = array.keyAt(index); + if (!compare(realValue, value)) { + fail("Bad array map key set: expected key " + realValue + + ", got " + value + " at index " + index); + } + index++; + } + + index = 0; + Collection valueCol = array.values(); + Iterator valueIt = valueCol.iterator(); + while (valueIt.hasNext()) { + Object value = valueIt.next(); + Object realValue = array.valueAt(index); + if (!compare(realValue, value)) { + fail("Bad array map value col: expected value " + realValue + + ", got " + value + " at index " + index); + } + index++; + } + } + + private static void compareBundles(Bundle bundle1, Bundle bundle2) { + Set<String> keySet1 = bundle1.keySet(); + Iterator<String> iterator1 = keySet1.iterator(); + while (iterator1.hasNext()) { + String key = iterator1.next(); + int value1 = bundle1.getInt(key); + if (bundle2.get(key) == null) { + fail("Bad Bundle: bundle2 didn't have expected key " + key); + } + int value2 = bundle2.getInt(key); + if (value1 != value2) { + fail("Bad Bundle: at key key " + key + " expected " + value1 + ", got " + value2); + } + } + Set<String> keySet2 = bundle2.keySet(); + Iterator<String> iterator2 = keySet2.iterator(); + while (iterator2.hasNext()) { + String key = iterator2.next(); + if (bundle1.get(key) == null) { + fail("Bad Bundle: bundle1 didn't have expected key " + key); + } + int value1 = bundle1.getInt(key); + int value2 = bundle2.getInt(key); + if (value1 != value2) { + fail("Bad Bundle: at key key " + key + " expected " + value1 + ", got " + value2); + } + } + } + + private static void dump(Map map, ArrayMap array) { + Log.e("test", "HashMap of " + map.size() + " entries:"); + Set<Map.Entry> mapSet = map.entrySet(); + for (Map.Entry entry : mapSet) { + Log.e("test", " " + entry.getKey() + " -> " + entry.getValue()); + } + Log.e("test", "ArrayMap of " + array.size() + " entries:"); + for (int i = 0; i < array.size(); i++) { + Log.e("test", " " + array.keyAt(i) + " -> " + array.valueAt(i)); + } + } + + private static void dump(ArrayMap map1, ArrayMap map2) { + Log.e("test", "ArrayMap of " + map1.size() + " entries:"); + for (int i = 0; i < map1.size(); i++) { + Log.e("test", " " + map1.keyAt(i) + " -> " + map1.valueAt(i)); + } + Log.e("test", "ArrayMap of " + map2.size() + " entries:"); + for (int i = 0; i < map2.size(); i++) { + Log.e("test", " " + map2.keyAt(i) + " -> " + map2.valueAt(i)); + } + } + + private static void dump(Bundle bundle1, Bundle bundle2) { + Log.e("test", "First Bundle of " + bundle1.size() + " entries:"); + Set<String> keys1 = bundle1.keySet(); + for (String key : keys1) { + Log.e("test", " " + key + " -> " + bundle1.get(key)); + } + Log.e("test", "Second Bundle of " + bundle2.size() + " entries:"); + Set<String> keys2 = bundle2.keySet(); + for (String key : keys2) { + Log.e("test", " " + key + " -> " + bundle2.get(key)); + } + } + + @Test + public void testBasicArrayMap() { + HashMap<ControlledHash, Integer> hashMap = new HashMap<>(); + ArrayMap<ControlledHash, Integer> arrayMap = new ArrayMap<>(); + Bundle bundle = new Bundle(); + + for (int i = 0; i < OPS.length; i++) { + Integer oldHash; + Integer oldArray; + ControlledHash key = KEYS[i] < 0 ? null : new ControlledHash(KEYS[i]); + String strKey = KEYS[i] < 0 ? null : Integer.toString(KEYS[i]); + switch (OPS[i]) { + case OP_ADD: + if (DEBUG) Log.i("test", "Adding key: " + key); + oldHash = hashMap.put(key, i); + oldArray = arrayMap.put(key, i); + bundle.putInt(strKey, i); + break; + case OP_REM: + if (DEBUG) Log.i("test", "Removing key: " + key); + oldHash = hashMap.remove(key); + oldArray = arrayMap.remove(key); + bundle.remove(strKey); + break; + default: + fail("Bad operation " + OPS[i] + " @ " + i); + return; + } + if (!compare(oldHash, oldArray)) { + String msg = "Bad result: expected " + oldHash + ", got " + oldArray; + Log.e("test", msg); + dump(hashMap, arrayMap); + fail(msg); + } + try { + validateArrayMap(arrayMap); + } catch (Throwable e) { + Log.e("test", e.getMessage()); + dump(hashMap, arrayMap); + throw e; + } + try { + compareMaps(hashMap, arrayMap); + } catch (Throwable e) { + Log.e("test", e.getMessage()); + dump(hashMap, arrayMap); + throw e; + } + Parcel parcel = Parcel.obtain(); + bundle.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + Bundle bundle2 = parcel.readBundle(); + try { + compareBundles(bundle, bundle2); + } catch (Throwable e) { + Log.e("test", e.getMessage()); + dump(bundle, bundle2); + throw e; + } + } + + arrayMap.put(new ControlledHash(50000), 100); + ControlledHash lookup = new ControlledHash(50000); + Iterator<ControlledHash> it = arrayMap.keySet().iterator(); + while (it.hasNext()) { + if (it.next().equals(lookup)) { + it.remove(); + } + } + if (arrayMap.containsKey(lookup)) { + String msg = "Bad map iterator: didn't remove test key"; + Log.e("test", msg); + dump(hashMap, arrayMap); + fail(msg); + } + } + + @Test + public void testCopyArrayMap() { + // map copy constructor test + ArrayMap newMap = new ArrayMap<Integer, String>(); + for (int i = 0; i < 10; ++i) { + newMap.put(i, String.valueOf(i)); + } + ArrayMap mapCopy = new ArrayMap(newMap); + if (!compare(mapCopy, newMap)) { + String msg = "ArrayMap copy constructor failure: expected " + + newMap + ", got " + mapCopy; + Log.e("test", msg); + dump(newMap, mapCopy); + fail(msg); + return; + } + } + + @Test + public void testEqualsArrayMap() { + ArrayMap<Integer, String> map1 = new ArrayMap<>(); + ArrayMap<Integer, String> map2 = new ArrayMap<>(); + HashMap<Integer, String> map3 = new HashMap<>(); + if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) { + fail("ArrayMap equals failure for empty maps " + map1 + ", " + + map2 + ", " + map3); + } + + for (int i = 0; i < 10; ++i) { + String value = String.valueOf(i); + map1.put(i, value); + map2.put(i, value); + map3.put(i, value); + } + if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) { + fail("ArrayMap equals failure for populated maps " + map1 + ", " + + map2 + ", " + map3); + } + + map1.remove(0); + if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) { + fail("ArrayMap equals failure for map size " + map1 + ", " + + map2 + ", " + map3); + } + + map1.put(0, "-1"); + if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) { + fail("ArrayMap equals failure for map contents " + map1 + ", " + + map2 + ", " + map3); + } + } + +// /** +// * Test creating a malformed array map with duplicated keys and that we will catch this when +// * unparcelling. +// */ +// @Test +// public void testDuplicateKeys() throws NoSuchMethodException, +// InvocationTargetException, IllegalAccessException, NoSuchFieldException { +// ArrayMap<String, Object> map1 = new ArrayMap(2); +// +// Method appendMethod = ArrayMap.class.getMethod("append", Object.class, Object.class); +// appendMethod.invoke(map1, Integer.toString(100000), "foo"); +// appendMethod.invoke(map1, Integer.toString(100000), "bar"); +// +// // Now parcel/unparcel, and verify we get the expected error. +// Parcel parcel = Parcel.obtain(); +// Method writeArrayMapMethod = Parcel.class.getMethod("writeArrayMap", ArrayMap.class); +// writeArrayMapMethod.invoke(parcel, map1); +// parcel.setDataPosition(0); +// ArrayMap<String, Object> map2 = new ArrayMap(2); +// +// try { +// Parcel.class.getMethod("readArrayMap", ArrayMap.class, ClassLoader.class).invoke( +// parcel, map2, null); +// } catch (InvocationTargetException e) { +// Throwable cause = e.getCause(); +// if (cause instanceof IllegalArgumentException) { +// // Good! +// return; +// } +// throw e; +// } +// +// String msg = "Didn't throw expected IllegalArgumentException"; +// Log.e("test", msg); +// dump(map1, map2); +// fail(msg); +// } + + private static void checkEntrySetToArray(ArrayMap<?, ?> testMap) { + try { + testMap.entrySet().toArray(); + fail(); + } catch (UnsupportedOperationException expected) { + } + + try { + Map.Entry<?, ?>[] entries = new Map.Entry[20]; + testMap.entrySet().toArray(entries); + fail(); + } catch (UnsupportedOperationException expected) { + } + } + + // http://b/32294038, Test ArrayMap.entrySet().toArray() + @Test + public void testEntrySetArray() { + // Create + ArrayMap<Integer, String> testMap = new ArrayMap<>(); + + // Test empty + checkEntrySetToArray(testMap); + + // Test non-empty + for (int i = 0; i < 10; ++i) { + testMap.put(i, String.valueOf(i)); + } + checkEntrySetToArray(testMap); + } + + @Test + public void testCanNotIteratePastEnd_entrySetIterator() { + Map<String, String> map = new ArrayMap<>(); + map.put("key 1", "value 1"); + map.put("key 2", "value 2"); + Set<Map.Entry<String, String>> expectedEntriesToIterate = new HashSet<>(Arrays.asList( + entryOf("key 1", "value 1"), + entryOf("key 2", "value 2") + )); + Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); + + // Assert iteration over the expected two entries in any order + assertTrue(iterator.hasNext()); + Map.Entry<String, String> firstEntry = copyOf(iterator.next()); + assertTrue(expectedEntriesToIterate.remove(firstEntry)); + + assertTrue(iterator.hasNext()); + Map.Entry<String, String> secondEntry = copyOf(iterator.next()); + assertTrue(expectedEntriesToIterate.remove(secondEntry)); + + assertFalse(iterator.hasNext()); + + try { + iterator.next(); + fail(); + } catch (NoSuchElementException expected) { + } + } + + private static <K, V> Map.Entry<K, V> entryOf(K key, V value) { + return new AbstractMap.SimpleEntry<>(key, value); + } + + private static <K, V> Map.Entry<K, V> copyOf(Map.Entry<K, V> entry) { + return entryOf(entry.getKey(), entry.getValue()); + } + + @Test + public void testCanNotIteratePastEnd_keySetIterator() { + Map<String, String> map = new ArrayMap<>(); + map.put("key 1", "value 1"); + map.put("key 2", "value 2"); + Set<String> expectedKeysToIterate = new HashSet<>(Arrays.asList("key 1", "key 2")); + Iterator<String> iterator = map.keySet().iterator(); + + // Assert iteration over the expected two keys in any order + assertTrue(iterator.hasNext()); + String firstKey = iterator.next(); + assertTrue(expectedKeysToIterate.remove(firstKey)); + + assertTrue(iterator.hasNext()); + String secondKey = iterator.next(); + assertTrue(expectedKeysToIterate.remove(secondKey)); + + assertFalse(iterator.hasNext()); + + try { + iterator.next(); + fail(); + } catch (NoSuchElementException expected) { + } + } + + @Test + public void testCanNotIteratePastEnd_valuesIterator() { + Map<String, String> map = new ArrayMap<>(); + map.put("key 1", "value 1"); + map.put("key 2", "value 2"); + Set<String> expectedValuesToIterate = new HashSet<>(Arrays.asList("value 1", "value 2")); + Iterator<String> iterator = map.values().iterator(); + + // Assert iteration over the expected two values in any order + assertTrue(iterator.hasNext()); + String firstValue = iterator.next(); + assertTrue(expectedValuesToIterate.remove(firstValue)); + + assertTrue(iterator.hasNext()); + String secondValue = iterator.next(); + assertTrue(expectedValuesToIterate.remove(secondValue)); + + assertFalse(iterator.hasNext()); + + try { + iterator.next(); + fail(); + } catch (NoSuchElementException expected) { + } + } + + @Test + public void testForEach() { + ArrayMap<String, Integer> map = new ArrayMap<>(); + + for (int i = 0; i < 50; ++i) { + map.put(Integer.toString(i), i * 10); + } + + // Make sure forEach goes through all of the elements. + HashMap<String, Integer> seen = new HashMap<>(); + map.forEach(seen::put); + compareMaps(seen, map); + } + + /** + * The entrySet Iterator returns itself from each call to {@code next()}. This is unusual + * behavior for {@link Iterator#next()}; this test ensures that any future change to this + * behavior is deliberate. + */ + @Test + public void testUnusualBehavior_eachEntryIsSameAsIterator_entrySetIterator() { + Map<String, String> map = new ArrayMap<>(); + map.put("key 1", "value 1"); + map.put("key 2", "value 2"); + Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); + + assertSame(iterator, iterator.next()); + assertSame(iterator, iterator.next()); + } + + @SuppressWarnings("SelfEquals") + @Test + public void testUnusualBehavior_equalsThrowsAfterRemove_entrySetIterator() { + Map<String, String> map = new ArrayMap<>(); + map.put("key 1", "value 1"); + map.put("key 2", "value 2"); + Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); + iterator.next(); + iterator.remove(); + try { + iterator.equals(iterator); + fail(); + } catch (IllegalStateException expected) { + } + } + + private static <T> void assertEqualsBothWays(T a, T b) { + assertEquals(a, b); + assertEquals(b, a); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test + public void testRemoveAll() { + final ArrayMap<Integer, String> map = new ArrayMap<>(); + for (Integer i : Arrays.asList(0, 1, 2, 3, 4, 5)) { + map.put(i, i.toString()); + } + + final ArrayMap<Integer, String> expectedMap = new ArrayMap<>(); + for (Integer i : Arrays.asList(2, 4)) { + expectedMap.put(i, String.valueOf(i)); + } + map.removeAll(Arrays.asList(0, 1, 3, 5, 6)); + if (!compare(map, expectedMap)) { + fail("ArrayMap removeAll failure, expect " + expectedMap + ", but " + map); + } + + map.removeAll(Collections.emptyList()); + if (!compare(map, expectedMap)) { + fail("ArrayMap removeAll failure for empty maps, expect " + expectedMap + ", but " + + map); + } + + map.removeAll(Arrays.asList(2, 4)); + if (!map.isEmpty()) { + fail("ArrayMap removeAll failure, expect empty, but " + map); + } + } + + @Test + public void testReplaceAll() { + final ArrayMap<Integer, Integer> map = new ArrayMap<>(); + final ArrayMap<Integer, Integer> expectedMap = new ArrayMap<>(); + final BiFunction<Integer, Integer, Integer> function = (k, v) -> 2 * v; + for (Integer i : Arrays.asList(0, 1, 2, 3, 4, 5)) { + map.put(i, i); + expectedMap.put(i, 2 * i); + } + + map.replaceAll(function); + if (!compare(map, expectedMap)) { + fail("ArrayMap replaceAll failure, expect " + expectedMap + ", but " + map); + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java new file mode 100644 index 000000000000..56544b4d5151 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.frameworktest; + +import static com.google.common.truth.Truth.assertThat; + +import android.util.Log; +import android.util.Slog; + +import org.junit.Test; + +/** + * Some basic tests for {@link android.util.Log}. + */ +public class LogTest { + @Test + public void testBasicLogging() { + Log.v("TAG", "Test v log"); + Log.d("TAG", "Test d log"); + Log.i("TAG", "Test i log"); + Log.w("TAG", "Test w log"); + Log.e("TAG", "Test e log"); + + Slog.v("TAG", "Test v slog"); + Slog.d("TAG", "Test d slog"); + Slog.i("TAG", "Test i slog"); + Slog.w("TAG", "Test w slog"); + Slog.e("TAG", "Test e slog"); + } + + @Test + public void testNativeMethods() { + assertThat(Log.isLoggable("mytag", Log.INFO)).isTrue(); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp new file mode 100644 index 000000000000..8c76a612020f --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp @@ -0,0 +1,141 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +// A library that simulates framework-all.jar +java_library { + name: "hoststubgen-test-tiny-framework", + installable: true, + host_supported: true, + srcs: ["tiny-framework/src/**/*.java"], + static_libs: [ + "hoststubgen-annotations", + ], +} + +// Create stub/impl jars from "hoststubgen-test-tiny-framework", using the following 3 rules. +java_genrule_host { + name: "hoststubgen-test-tiny-framework-host", + defaults: ["hoststubgen-command-defaults"], + cmd: hoststubgen_common_options + + "--in-jar $(location :hoststubgen-test-tiny-framework) " + + "--policy-override-file $(location policy-override-tiny-framework.txt) ", + srcs: [ + ":hoststubgen-test-tiny-framework", + "policy-override-tiny-framework.txt", + ], +} + +java_genrule_host { + name: "hoststubgen-test-tiny-framework-host-stub", + cmd: "cp $(in) $(out)", + srcs: [ + ":hoststubgen-test-tiny-framework-host{host_stub.jar}", + ], + out: [ + "host_stub.jar", + ], +} + +java_genrule_host { + name: "hoststubgen-test-tiny-framework-host-impl", + cmd: "cp $(in) $(out)", + srcs: [ + ":hoststubgen-test-tiny-framework-host{host_impl.jar}", + ], + out: [ + "host_impl.jar", + ], +} + +// Compile the test jar, using 2 rules. +// 1. Build the test against the stub. +java_library_host { + name: "hoststubgen-test-tiny-test-lib", + srcs: ["tiny-test/src/**/*.java"], + + libs: [ + "hoststubgen-test-tiny-framework-host-stub", + ], + static_libs: [ + "junit", + "truth-prebuilt", + + // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/ + "platform-test-annotations", + ], + visibility: ["//visibility:private"], +} + +// 2. Link "hoststubgen-test-tiny-test-lib" with necessary runtime dependencies, so it can be +// executed stand-alone. +java_test_host { + name: "hoststubgen-test-tiny-test", + test_config: "AndroidTest-host.xml", + static_libs: [ + "hoststubgen-test-tiny-test-lib", + "hoststubgen-helper-runtime", + "hoststubgen-test-tiny-framework-host-impl", + ], + test_suites: ["general-tests"], +} + +// Dump the original, stub and impl jars as text files. +// We use them in test-and-update-golden.sh. +java_genrule_host { + name: "hoststubgen-test-tiny-framework-orig-dump", + defaults: ["hoststubgen-jar-dump-defaults"], + srcs: [ + ":hoststubgen-test-tiny-framework", + ], + out: [ + "01-hoststubgen-test-tiny-framework-orig-dump.txt", + ], + visibility: ["//visibility:private"], +} + +java_genrule_host { + name: "hoststubgen-test-tiny-framework-host-stub-dump", + defaults: ["hoststubgen-jar-dump-defaults"], + srcs: [ + ":hoststubgen-test-tiny-framework-host-stub", + ], + out: [ + "02-hoststubgen-test-tiny-framework-host-stub-dump.txt", + ], + visibility: ["//visibility:private"], +} + +java_genrule_host { + name: "hoststubgen-test-tiny-framework-host-impl-dump", + defaults: ["hoststubgen-jar-dump-defaults"], + srcs: [ + ":hoststubgen-test-tiny-framework-host-impl", + ], + out: [ + "03-hoststubgen-test-tiny-framework-host-impl-dump.txt", + ], + visibility: ["//visibility:private"], +} + +// Run it with `atest`. Compare the dump of the jar files to the golden output. +python_test_host { + name: "tiny-framework-dump-test", + srcs: [ + "tiny-framework-dump-test.py", + ], + data: [ + "golden-output/*.txt", + ], + java_data: [ + "hoststubgen-test-tiny-framework-host-stub-dump", + "hoststubgen-test-tiny-framework-host-impl-dump", + "hoststubgen-test-tiny-framework-orig-dump", + ], + test_suites: ["general-tests"], +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml b/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml new file mode 100644 index 000000000000..84aad69c33bc --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- [Ravenwood] Copied from $ANDROID_BUILD_TOP/cts/hostsidetests/devicepolicy/AndroidTest.xml --> +<configuration description="HostStubGen sample test"> + <option name="test-suite-tag" value="ravenwood" /> + <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" /> + <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> + + <test class="com.android.tradefed.testtype.IsolatedHostTest" > + <option name="jar" value="hoststubgen-test-tiny-test.jar" /> + </test> +</configuration> diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md new file mode 100644 index 000000000000..f3c0450d42a3 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md @@ -0,0 +1,27 @@ +# HostStubGen: tiny-framework test + +This directory contains a small classes that "simulates" framework.jar, and tests against it. + +This test doesn't use the actual android framework code. + +## How to run + +- With `atest`. This is the proper way to run it, but `atest` has known problems that may + affect the result. If you see weird problems, try the next `run-ravenwood-test` command. + +``` +$ atest hoststubgen-test-tiny-test +``` + +- With `run-ravenwood-test` should work too. This is the proper way to run it. + +``` +$ run-ravenwood-test hoststubgen-test-tiny-test +``` + +- `run-test-manually.sh` also run the test, but it builds the stub/impl jars and the test without + using the build system. This is useful for debugging the tool. + +``` +$ ./run-test-manually.sh +```
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh new file mode 100755 index 000000000000..4d588694b8da --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh @@ -0,0 +1,134 @@ +#!/bin/bash +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +help() { + cat <<'EOF' + + diff-and-update-golden.sh [OPTIONS] + + Compare the generated jar files from tiny-framework to the "golden" files. + + OPTIONS: + -u: Update the golden files. + + -3: Run `meld` to compare original, stub and impl jar files in 3-way diff. + This is useful to visualize the exact differences between 3 jar files. + + -2: Run `meld` to compare original <-> impl, and impl <-> stub as two different diffs. +EOF +} + +source "${0%/*}"/../../common.sh + +SCRIPT_NAME="${0##*/}" + +GOLDEN_DIR=golden-output +mkdir -p $GOLDEN_DIR + +DIFF_CMD=${DIFF:-diff -u --ignore-blank-lines --ignore-space-change} + +update=0 +three_way=0 +two_way=0 +while getopts "u32" opt; do +case "$opt" in + u) + update=1 + ;; + 3) + three_way=1 + ;; + 2) + two_way=1 + ;; + '?') + help + exit 1 + ;; +esac +done +shift $(($OPTIND - 1)) + + +# Build the dump files, which are the input of this test. +run m tiny-framework-dump-test + + +# Get the path to the generate text files. (not the golden files.) +# We get them from $OUT/module-info.json + +files=( +$(python3 -c ' +import sys +import os +import json + +with open(sys.argv[1], "r") as f: + data = json.load(f) + + # Equivalent to: jq -r '.["tiny-framework-dump-test"]["installed"][]' + for path in data["tiny-framework-dump-test"]["installed"]: + + if "golden-output" in path: + continue + if path.endswith(".txt"): + print(os.getenv("ANDROID_BUILD_TOP") + "/" + path) +' $OUT/module-info.json) +) + +# Next, compare each file and update them in $GOLDEN_DIR + +any_file_changed=0 + +for file in ${files[*]} ; do + name=$(basename $file) + echo "# Checking $name ..." + + file_changed=0 + if run $DIFF_CMD $GOLDEN_DIR/$name $file; then + : # No diff + else + file_changed=1 + any_file_changed=1 + fi + + if (( $update && $file_changed )) ; then + echo "# Updating $name ..." + run cp $file $GOLDEN_DIR/$name + fi +done + +if (( $three_way )) ; then + echo "# Running 3-way diff with meld..." + run meld ${files[*]} & +fi + +if (( $two_way )) ; then + echo "# Running meld..." + run meld --diff ${files[0]} ${files[1]} --diff ${files[1]} ${files[2]} +fi + +if (( $any_file_changed == 0 )) ; then + echo "$SCRIPT_NAME: Success: no changes detected." + exit 0 +else + if (( $update )) ; then + echo "$SCRIPT_NAME: Warning: golden files have been updated." + exit 2 + else + echo "$SCRIPT_NAME: Failure: changes detected. See above diff for the details." + exit 3 + fi +fi diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt new file mode 100644 index 000000000000..1aa485963f97 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt @@ -0,0 +1,1671 @@ +## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class + Compiled from "HostSideTestClassLoadHook.java" +public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestClassLoadHook.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestKeep.class + Compiled from "HostSideTestKeep.java" +public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestKeep.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class + Compiled from "HostSideTestNativeSubstitutionClass.java" +public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestNativeSubstitutionClass + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestNativeSubstitutionClass.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRemove.class + Compiled from "HostSideTestRemove.java" +public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRemove + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestRemove.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestStub.class + Compiled from "HostSideTestStub.java" +public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestStub + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestStub.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestSubstitute.class + Compiled from "HostSideTestSubstitute.java" +public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestSubstitute + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String suffix(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestSubstitute.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestThrow.class + Compiled from "HostSideTestThrow.java" +public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestThrow + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestThrow.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class + Compiled from "HostSideTestWholeClassKeep.java" +public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestWholeClassKeep.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class + Compiled from "HostSideTestWholeClassStub.java" +public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestWholeClassStub + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestWholeClassStub.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/tests/HostSideTestSuppress.class + Compiled from "HostSideTestSuppress.java" +public interface android.hosttest.annotation.tests.HostSideTestSuppress extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/tests/HostSideTestSuppress + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestSuppress.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD] + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 3 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; + + public static int getOneKeep(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: iconst_1 + 1: ireturn + LineNumberTable: + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: iconst_1 + 1: ireturn + LineNumberTable: + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +SourceFile: "TinyFrameworkCallerCheck.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 4 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; + + public static int getOne_withCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I + 3: ireturn + LineNumberTable: + + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + 3: ireturn + LineNumberTable: +} +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class + Compiled from "TinyFrameworkClassAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 3, methods: 10, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 6 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_1 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + public void toBeRemoved(java.lang.String); + descriptor: (Ljava/lang/String;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + 7: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 8 1 foo Ljava/lang/String; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestRemove + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String not supported on host side + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 10 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestSubstitute( + suffix="_host" + ) + + public int addTwo_host(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 1 value I + + public static native int nativeAddThree(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestSubstitute( + suffix="_host" + ) + + public static int nativeAddThree_host(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: ldc #x // String This value shouldn\'t be seen on the host side. + 2: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestThrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +SourceFile: "TinyFrameworkClassAnnotations.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class + Compiled from "TinyFrameworkClassClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 3, methods: 10, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 6 1 value I + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_1 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 4 1 value I + + public void toBeRemoved(java.lang.String); + descriptor: (Ljava/lang/String;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + 7: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 8 1 foo Ljava/lang/String; + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String not supported on host side + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 10 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestSubstitute( + suffix="_host" + ) + + public int addTwo_host(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 4 1 value I + + public static native int nativeAddThree(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestSubstitute( + suffix="_host" + ) + + public static int nativeAddThree_host(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: ldc #x // String This value shouldn\'t be seen on the host side. + 2: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; +} +SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class + Compiled from "TinyFrameworkClassLoadHook.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 2 + public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; + descriptor: Ljava/util/Set; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook; + + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set; + 3: aload_0 + 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z + 9: pop + 10: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class; + LocalVariableTypeTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class<*>; + Signature: #x // (Ljava/lang/Class<*>;)V + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class java/util/HashSet + 3: dup + 4: invokespecial #x // Method java/util/HashSet."<init>":()V + 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set; + 10: return + LineNumberTable: +} +SourceFile: "TinyFrameworkClassLoadHook.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class + Compiled from "TinyFrameworkClassWithInitializer.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 2 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: iconst_1 + 1: putstatic #x // Field sInitialized:Z + 4: return + LineNumberTable: +} +SourceFile: "TinyFrameworkClassWithInitializer.java" +RuntimeInvisibleAnnotations: + 0: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) + 1: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class + Compiled from "TinyFrameworkExceptionTester.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 2 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester; + + public static int testException(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=0 + 0: new #x // class java/lang/IllegalStateException + 3: dup + 4: ldc #x // String Inner exception + 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V + 9: athrow + 10: astore_0 + 11: new #x // class java/lang/RuntimeException + 14: dup + 15: ldc #x // String Outer exception + 17: aload_0 + 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V + 21: athrow + Exception table: + from to target type + 0 10 10 Class java/lang/Exception + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 11 0 e Ljava/lang/Exception; + StackMapTable: number_of_entries = 1 + frame_type = 74 /* same_locals_1_stack_item */ + stack = [ class java/lang/Exception ] +} +SourceFile: "TinyFrameworkExceptionTester.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class + Compiled from "TinyFrameworkForTextPolicy.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + super_class: #x // java/lang/Object + interfaces: 0, fields: 3, methods: 10, attributes: 1 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 6 1 value I + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_1 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 4 1 value I + + public void toBeRemoved(java.lang.String); + descriptor: (Ljava/lang/String;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + 7: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 8 1 foo Ljava/lang/String; + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String not supported on host side + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 10 1 value I + + public int addTwo_host(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 4 1 value I + + public static native int nativeAddThree(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static int addThree_host(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: ldc #x // String This value shouldn\'t be seen on the host side. + 2: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; +} +SourceFile: "TinyFrameworkForTextPolicy.java" +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class + Compiled from "TinyFrameworkNative.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 5, attributes: 2 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + + public static native int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static int nativeAddTwo_should_be_like_this(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=1, args_size=1 + 0: iload_0 + 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + 4: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 arg I + + public static native long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static long nativeLongPlus_should_be_like_this(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + 0: lload_0 + 1: lload_2 + 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + 5: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 arg1 J + 0 6 2 arg2 J +} +SourceFile: "TinyFrameworkNative.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestNativeSubstitutionClass( + value="TinyFrameworkNative_host" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class + Compiled from "TinyFrameworkNative_host.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 2 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host; + + public static int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 arg I + + public static long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + 0: lload_0 + 1: lload_2 + 2: ladd + 3: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 arg1 J + 0 4 2 arg2 J +} +SourceFile: "TinyFrameworkNative_host.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 1, methods: 3, attributes: 5 + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: iconst_1 + 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method get:()Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 5 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: iconst_2 + 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method get:()Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 1, methods: 3, attributes: 5 + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: iconst_3 + 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method get:()Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 5 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: iconst_4 + 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method get:()Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 3 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iload_1 + 6: putfield #x // Field value:I + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass; + 0 10 1 x I +} +SourceFile: "TinyFrameworkNestedClasses.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: aload_0 + 10: iconst_5 + 11: putfield #x // Field value:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass; + 0 15 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; +} +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 5 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: bipush 7 + 2: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 5: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method get:()Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: bipush 6 + 7: putfield #x // Field value:I + 10: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V + 7: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; +} +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + interfaces: 0, fields: 0, methods: 1, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V + 5: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass; + 0 6 1 x I +} +SourceFile: "TinyFrameworkNestedClasses.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 4, attributes: 4 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + 8: dup + 9: aload_0 + 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + 16: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + 3: dup + 4: aload_0 + 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + 8: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 9 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V + 7: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V + 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + 10: return + LineNumberTable: +} +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt new file mode 100644 index 000000000000..6e1528aedc1e --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt @@ -0,0 +1,837 @@ +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 4 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 5 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int getOne_withCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class + Compiled from "TinyFrameworkClassAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 5, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +SourceFile: "TinyFrameworkClassAnnotations.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class + Compiled from "TinyFrameworkClassClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 3, methods: 8, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public void toBeRemoved(java.lang.String); + descriptor: (Ljava/lang/String;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class + Compiled from "TinyFrameworkClassLoadHook.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 3 + public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; + descriptor: Ljava/util/Set; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + Signature: #x // (Ljava/lang/Class<*>;)V + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkClassLoadHook.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class + Compiled from "TinyFrameworkClassWithInitializer.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 3 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkClassWithInitializer.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) + 1: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class + Compiled from "TinyFrameworkExceptionTester.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int testException(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkExceptionTester.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class + Compiled from "TinyFrameworkForTextPolicy.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 5, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkForTextPolicy.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class + Compiled from "TinyFrameworkNative.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 5, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static native int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static int nativeAddTwo_should_be_like_this(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static native long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static long nativeLongPlus_should_be_like_this(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=4, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkNative.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestNativeSubstitutionClass( + value="TinyFrameworkNative_host" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 1, attributes: 5 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +InnerClasses: + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 5 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + interfaces: 0, fields: 0, methods: 1, attributes: 4 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 4, attributes: 5 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt new file mode 100644 index 000000000000..5672e9c36706 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt @@ -0,0 +1,1774 @@ +## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class + Compiled from "HostSideTestClassLoadHook.java" +public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestClassLoadHook.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestKeep.class + Compiled from "HostSideTestKeep.java" +public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestKeep.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class + Compiled from "HostSideTestNativeSubstitutionClass.java" +public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestNativeSubstitutionClass + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestNativeSubstitutionClass.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRemove.class + Compiled from "HostSideTestRemove.java" +public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRemove + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestRemove.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestStub.class + Compiled from "HostSideTestStub.java" +public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestStub + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestStub.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestSubstitute.class + Compiled from "HostSideTestSubstitute.java" +public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestSubstitute + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String suffix(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestSubstitute.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestThrow.class + Compiled from "HostSideTestThrow.java" +public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestThrow + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestThrow.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class + Compiled from "HostSideTestWholeClassKeep.java" +public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestWholeClassKeep.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class + Compiled from "HostSideTestWholeClassStub.java" +public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestWholeClassStub + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestWholeClassStub.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 4 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; + + public static int getOneKeep(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + 2: ldc #x // String getOneKeep + 4: ldc #x // String ()I + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iconst_1 + 16: ireturn + LineNumberTable: + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: iconst_1 + 1: ireturn + LineNumberTable: + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 5 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; + + public static int getOne_withCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I + 3: ireturn + LineNumberTable: + + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + 3: ireturn + LineNumberTable: +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class + Compiled from "TinyFrameworkClassAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 8, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded + 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + 7: return + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 6 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + 2: ldc #x // String addOneInner + 4: ldc #x // String (I)I + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iload_1 + 16: iconst_1 + 17: iadd + 18: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 15 4 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 1 value I + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + 2: ldc #x // String unsupportedMethod + 4: ldc #x // String ()Ljava/lang/String; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V + 18: new #x // class java/lang/RuntimeException + 21: dup + 22: ldc #x // String Unreachable + 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 27: athrow + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestThrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +SourceFile: "TinyFrameworkClassAnnotations.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class + Compiled from "TinyFrameworkClassClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 3, methods: 8, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 6 1 value I + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_1 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 4 1 value I + + public void toBeRemoved(java.lang.String); + descriptor: (Ljava/lang/String;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + 7: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 8 1 foo Ljava/lang/String; + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 4 1 value I + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: ldc #x // String This value shouldn\'t be seen on the host side. + 2: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; +} +SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class + Compiled from "TinyFrameworkClassLoadHook.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 3 + public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; + descriptor: Ljava/util/Set; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook; + + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set; + 3: aload_0 + 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z + 9: pop + 10: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class; + LocalVariableTypeTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class<*>; + Signature: #x // (Ljava/lang/Class<*>;)V + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class java/util/HashSet + 3: dup + 4: invokespecial #x // Method java/util/HashSet."<init>":()V + 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set; + 10: return + LineNumberTable: +} +SourceFile: "TinyFrameworkClassLoadHook.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class + Compiled from "TinyFrameworkClassWithInitializer.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 3 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer + 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded + 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + 7: iconst_1 + 8: putstatic #x // Field sInitialized:Z + 11: return + LineNumberTable: +} +SourceFile: "TinyFrameworkClassWithInitializer.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) + 1: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class + Compiled from "TinyFrameworkExceptionTester.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester; + + public static int testException(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=0 + 0: new #x // class java/lang/IllegalStateException + 3: dup + 4: ldc #x // String Inner exception + 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V + 9: athrow + 10: astore_0 + 11: new #x // class java/lang/RuntimeException + 14: dup + 15: ldc #x // String Outer exception + 17: aload_0 + 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V + 21: athrow + Exception table: + from to target type + 0 10 10 Class java/lang/Exception + StackMapTable: number_of_entries = 1 + frame_type = 74 /* same_locals_1_stack_item */ + stack = [ class java/lang/Exception ] + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 11 0 e Ljava/lang/Exception; +} +SourceFile: "TinyFrameworkExceptionTester.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class + Compiled from "TinyFrameworkForTextPolicy.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 8, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded + 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + 7: return + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 6 1 value I + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + 2: ldc #x // String addOneInner + 4: ldc #x // String (I)I + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iload_1 + 16: iconst_1 + 17: iadd + 18: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 15 4 1 value I + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 4 1 value I + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + 2: ldc #x // String unsupportedMethod + 4: ldc #x // String ()Ljava/lang/String; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V + 18: new #x // class java/lang/RuntimeException + 21: dup + 22: ldc #x // String Unreachable + 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 27: athrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; +} +SourceFile: "TinyFrameworkForTextPolicy.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class + Compiled from "TinyFrameworkNative.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 5, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + + public static int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=1, args_size=1 + 0: iload_0 + 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + 4: ireturn + + public static int nativeAddTwo_should_be_like_this(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=1, args_size=1 + 0: iload_0 + 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + 4: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 arg I + + public static long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + 0: lload_0 + 1: lload_2 + 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + 5: lreturn + + public static long nativeLongPlus_should_be_like_this(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + 0: lload_0 + 1: lload_2 + 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + 5: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 arg1 J + 0 6 2 arg2 J +} +SourceFile: "TinyFrameworkNative.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestNativeSubstitutionClass( + value="TinyFrameworkNative_host" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class + Compiled from "TinyFrameworkNative_host.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + 2: ldc #x // String <init> + 4: ldc #x // String ()V + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokespecial #x // Method java/lang/Object."<init>":()V + 19: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host; + + public static int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + 2: ldc #x // String nativeAddTwo + 4: ldc #x // String (I)I + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iload_0 + 16: iconst_2 + 17: iadd + 18: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 4 0 arg I + + public static long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + 2: ldc #x // String nativeLongPlus + 4: ldc #x // String (JJ)J + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: lload_0 + 16: lload_2 + 17: ladd + 18: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 4 0 arg1 J + 15 4 2 arg2 J +} +SourceFile: "TinyFrameworkNative_host.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 1, methods: 3, attributes: 6 + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Integer; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iconst_1 + 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Object; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokevirtual #x // Method get:()Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 6 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Integer; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iconst_2 + 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Object; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokevirtual #x // Method get:()Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 1, methods: 3, attributes: 6 + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Integer; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iconst_3 + 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Object; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokevirtual #x // Method get:()Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 6 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Integer; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iconst_4 + 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Object; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokevirtual #x // Method get:()Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iload_1 + 6: putfield #x // Field value:I + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass; + 0 10 1 x I +} +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 1, attributes: 5 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: aload_0 + 10: iconst_5 + 11: putfield #x // Field value:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass; + 0 15 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; +} +InnerClasses: + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 6 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Integer; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: bipush 7 + 17: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 20: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Object; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokevirtual #x // Method get:()Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 5 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: bipush 6 + 7: putfield #x // Field value:I + 10: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V + 7: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + interfaces: 0, fields: 0, methods: 1, attributes: 4 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V + 5: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass; + 0 6 1 x I +} +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 4, attributes: 5 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + 8: dup + 9: aload_0 + 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + 16: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + 3: dup + 4: aload_0 + 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + 8: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 9 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V + 7: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V + 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + 10: return + LineNumberTable: +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt new file mode 100644 index 000000000000..079d2a84e498 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt @@ -0,0 +1,17 @@ +class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy stub + field stub stub + field keep keep + # field remove remove # Implicitly remove + method <init> ()V stub + method addOne (I)I stub + method addOneInner (I)I keep + method toBeRemoved (Ljava/lang/String;)V remove + method addTwo (I)I @addTwo_host + # method addTwo_host (I)I # used as a substitute + method nativeAddThree (I)I @addThree_host + # method addThree_host (I)I # used as a substitute + method unsupportedMethod ()Ljava/lang/String; throw + method visibleButUsesUnsupportedMethod ()Ljava/lang/String; stub + +# Class load hook +class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy ~com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh new file mode 100755 index 000000000000..fd486468a1a7 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh @@ -0,0 +1,132 @@ +#!/bin/bash +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +source "${0%/*}"/../../common.sh + +# This scripts run the "tiny-framework" test, but does most stuff from the command line, using +# the native java and javac commands. +# This is useful to + + +debug=0 +while getopts "d" opt; do +case "$opt" in + d) debug=1 ;; +esac +done +shift $(($OPTIND - 1)) + + +out=out + +rm -fr $out +mkdir -p $out + +HOSTSTUBGEN=hoststubgen + +# Rebuild the tool and the dependencies. These are the only things we build with the build system. +run m $HOSTSTUBGEN hoststubgen-annotations hoststubgen-helper-runtime truth-prebuilt junit + + +# Build tiny-framework + +tiny_framework_classes=$out/tiny-framework/classes/ +tiny_framework_jar=$out/tiny-framework.jar +tiny_framework_host_stub_jar=$out/tiny-framework_host_stub.jar +tiny_framework_host_impl_jar=$out/tiny-framework_host_impl.jar + +tiny_test_classes=$out/tiny-test/classes/ +tiny_test_jar=$out/tiny-test.jar + +framework_compile_classpaths=( + $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/hoststubgen-annotations/android_common/javac/hoststubgen-annotations.jar +) + +test_compile_classpaths=( + $SOONG_INT/external/junit/junit/android_common/combined/junit.jar + $SOONG_INT/prebuilts/tools/common/m2/truth-prebuilt/android_common/combined/truth-prebuilt.jar +) + +test_runtime_classpaths=( + $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/hoststubgen-helper-runtime/linux_glibc_common/javac/hoststubgen-helper-runtime.jar +) + +# This suite runs all tests in the JAR. +test_classes=(com.android.hoststubgen.hosthelper.HostTestSuite) + +# Uncomment this to run a specific test. +# tests=(com.android.hoststubgen.test.tinyframework.TinyFrameworkBenchmark) + + +# Build tiny-framework.jar +echo "# Building tiny-framework..." +run $JAVAC \ + -cp $( \ + join : \ + ${framework_compile_classpaths[@]} \ + ) \ + -d $tiny_framework_classes \ + tiny-framework/src/**/*.java + +run $JAR cvf $tiny_framework_jar \ + -C $tiny_framework_classes . + +# Build stub/impl jars +echo "# Generating the stub and impl jars..." +run $HOSTSTUBGEN \ + @../hoststubgen-standard-options.txt \ + --in-jar $tiny_framework_jar \ + --out-stub-jar $tiny_framework_host_stub_jar \ + --out-impl-jar $tiny_framework_host_impl_jar \ + --policy-override-file policy-override-tiny-framework.txt \ + --gen-keep-all-file out/tiny-framework_keep_all.txt \ + --gen-input-dump-file out/tiny-framework_dump.txt \ + $HOSTSTUBGEN_OPTS + +# Extract the jar files, so we can look into them. +extract $tiny_framework_host_stub_jar $tiny_framework_host_impl_jar + +# Build the test +echo "# Building tiny-test..." +run $JAVAC \ + -cp $( \ + join : \ + $tiny_framework_host_stub_jar \ + "${test_compile_classpaths[@]}" \ + ) \ + -d $tiny_test_classes \ + tiny-test/src/**/*.java + +run $JAR cvf $tiny_test_jar \ + -C $tiny_test_classes . + +if (( $debug )) ; then + JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8700" +fi + +# Run the test +echo "# Running tiny-test..." +run $JAVA \ + $JAVA_OPTS \ + -cp $( \ + join : \ + $tiny_test_jar \ + $tiny_framework_host_impl_jar \ + "${test_compile_classpaths[@]}" \ + "${test_runtime_classpaths[@]}" \ + ) \ + org.junit.runner.JUnitCore \ + ${test_classes[@]} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py new file mode 100755 index 000000000000..cee29dcd1d59 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py @@ -0,0 +1,64 @@ +#!/usr/bin/python3 +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Compare the tiny-framework JAR dumps to the golden files. + +import sys +import os +import unittest +import subprocess + +GOLDEN_DIR = 'golden-output' + +# Run diff. +def run_diff(file1, file2): + command = ['diff', '-u', '--ignore-blank-lines', '--ignore-space-change', file1, file2] + print(' '.join(command)) + result = subprocess.run(command, stderr = sys.stdout) + + success = result.returncode == 0 + + if success: + print(f'No diff found.') + else: + print(f'Fail: {file1} and {file2} are different.') + + return success + + +# Check one golden file. +def check_one_file(filename): + print(f'= Checking file: {filename}') + return run_diff(os.path.join(GOLDEN_DIR, filename), filename) + +class TestWithGoldenOutput(unittest.TestCase): + + # Test to check the generated jar files to the golden output. + def test_compare_to_golden(self): + files = os.listdir(GOLDEN_DIR) + files.sort() + + print(f"Golden files: {files}") + success = True + + for file in files: + if not check_one_file(file): + success = False + + if not success: + self.fail('Some files are different. See stdout log for more details.') + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java new file mode 100644 index 000000000000..f53020771cc3 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestKeep; +import android.hosttest.annotation.HostSideTestStub; +import android.hosttest.annotation.HostSideTestWholeClassStub; + +/** + * Used by the benchmark. + */ +@HostSideTestWholeClassStub +public class TinyFrameworkCallerCheck { + + /** + * This method uses an inner method (which has the caller check). + * + * Benchmark result: 768ns + */ + public static int getOne_withCheck() { + return Impl.getOneKeep(); + } + + /** + * This method doesn't have any caller check. + * + * Benchmark result: 2ns + */ + public static int getOne_noCheck() { + return Impl.getOneStub(); + } + + private static class Impl { + @HostSideTestKeep + public static int getOneKeep() { + return 1; + } + + @HostSideTestStub + public static int getOneStub() { + return 1; + } + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java new file mode 100644 index 000000000000..ab387e0938c3 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestClassLoadHook; +import android.hosttest.annotation.HostSideTestKeep; +import android.hosttest.annotation.HostSideTestRemove; +import android.hosttest.annotation.HostSideTestStub; +import android.hosttest.annotation.HostSideTestSubstitute; +import android.hosttest.annotation.HostSideTestThrow; + +/** + * Test without class-wide annotations. + */ +@HostSideTestStub +@HostSideTestClassLoadHook( + "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded") +public class TinyFrameworkClassAnnotations { + @HostSideTestStub + public TinyFrameworkClassAnnotations() { + } + + @HostSideTestStub + public int stub = 1; + + @HostSideTestKeep + public int keep = 2; + + // Members will be deleted by default. + // Deleted fields cannot have an initial value, because otherwise .ctor will fail to set it at + // runtime. + public int remove; + + @HostSideTestStub + public int addOne(int value) { + return addOneInner(value); + } + + @HostSideTestKeep + public int addOneInner(int value) { + return value + 1; + } + + @HostSideTestRemove // Explicitly remove + public void toBeRemoved(String foo) { + throw new RuntimeException(); + } + + @HostSideTestStub + @HostSideTestSubstitute(suffix = "_host") + public int addTwo(int value) { + throw new RuntimeException("not supported on host side"); + } + + public int addTwo_host(int value) { + return value + 2; + } + + @HostSideTestStub + @HostSideTestSubstitute(suffix = "_host") + public static native int nativeAddThree(int value); + + public static int nativeAddThree_host(int value) { + return value + 3; + } + + @HostSideTestThrow + public String unsupportedMethod() { + return "This value shouldn't be seen on the host side."; + } + + @HostSideTestStub + public String visibleButUsesUnsupportedMethod() { + return unsupportedMethod(); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java new file mode 100644 index 000000000000..145b65a98d7b --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestStub; +import android.hosttest.annotation.HostSideTestSubstitute; +import android.hosttest.annotation.HostSideTestWholeClassStub; + +@HostSideTestWholeClassStub +public class TinyFrameworkClassClassWideAnnotations { + public TinyFrameworkClassClassWideAnnotations() { + } + + public int stub = 1; + + public int keep = 2; + + // Cannot have an initial value, because otherwise .ctor will fail to set it at runtime. + public int remove; + + // @Stub + public int addOne(int value) { + return addOneInner(value); + } + + // @Keep + public int addOneInner(int value) { + return value + 1; + } + + // @Remove + public void toBeRemoved(String foo) { + throw new RuntimeException(); + } + + @HostSideTestStub + @HostSideTestSubstitute(suffix = "_host") + public int addTwo(int value) { + throw new RuntimeException("not supported on host side"); + } + + public int addTwo_host(int value) { + return value + 2; + } + + @HostSideTestStub + @HostSideTestSubstitute(suffix = "_host") + public static native int nativeAddThree(int value); + + public static int nativeAddThree_host(int value) { + return value + 3; + } + + public String unsupportedMethod() { + return "This value shouldn't be seen on the host side."; + } + + public String visibleButUsesUnsupportedMethod() { + return unsupportedMethod(); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java new file mode 100644 index 000000000000..98fc6349cdc9 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestWholeClassStub; + +import java.util.HashSet; +import java.util.Set; + +@HostSideTestWholeClassStub +public class TinyFrameworkClassLoadHook { + private TinyFrameworkClassLoadHook() { + } + + public static final Set<Class<?>> sLoadedClasses = new HashSet<>(); + + public static void onClassLoaded(Class<?> clazz) { + sLoadedClasses.add(clazz); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java new file mode 100644 index 000000000000..53cfdf6cd412 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestClassLoadHook; +import android.hosttest.annotation.HostSideTestWholeClassStub; + +@HostSideTestClassLoadHook( + "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded") +@HostSideTestWholeClassStub +public class TinyFrameworkClassWithInitializer { + static { + sInitialized = true; + } + + public static boolean sInitialized; +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java new file mode 100644 index 000000000000..909d3b440f50 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestWholeClassStub; + +@HostSideTestWholeClassStub +public class TinyFrameworkExceptionTester { + public static int testException() { + try { + throw new IllegalStateException("Inner exception"); + } catch (Exception e) { + throw new RuntimeException("Outer exception", e); + } + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java new file mode 100644 index 000000000000..bde7c35dc294 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +/** + * Class for testing the "text policy" file. + */ +public class TinyFrameworkForTextPolicy { + public TinyFrameworkForTextPolicy() { + } + + public int stub = 1; + + public int keep = 2; + + // Removed fields cannot have an initial value, because otherwise .ctor will fail to set it at + // runtime. + public int remove; + + public int addOne(int value) { + return addOneInner(value); + } + + public int addOneInner(int value) { + return value + 1; + } + + public void toBeRemoved(String foo) { + throw new RuntimeException(); + } + + public int addTwo(int value) { + throw new RuntimeException("not supported on host side"); + } + + public int addTwo_host(int value) { + return value + 2; + } + + public static native int nativeAddThree(int value); + + public static int addThree_host(int value) { + return value + 3; + } + + public String unsupportedMethod() { + return "This value shouldn't be seen on the host side."; + } + + public String visibleButUsesUnsupportedMethod() { + return unsupportedMethod(); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java new file mode 100644 index 000000000000..c151dcc9c90e --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestNativeSubstitutionClass; +import android.hosttest.annotation.HostSideTestWholeClassStub; + +@HostSideTestWholeClassStub +@HostSideTestNativeSubstitutionClass("TinyFrameworkNative_host") +public class TinyFrameworkNative { + public static native int nativeAddTwo(int arg); + + public static int nativeAddTwo_should_be_like_this(int arg) { + return TinyFrameworkNative_host.nativeAddTwo(arg); + } + + public static native long nativeLongPlus(long arg1, long arg2); + + public static long nativeLongPlus_should_be_like_this(long arg1, long arg2) { + return TinyFrameworkNative_host.nativeLongPlus(arg1, arg2); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java new file mode 100644 index 000000000000..48f7dea8c66c --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestWholeClassKeep; + +// TODO: This annotation shouldn't be needed. +// We should infer it from HostSideTestNativeSubstitutionClass. +@HostSideTestWholeClassKeep +public class TinyFrameworkNative_host { + public static int nativeAddTwo(int arg) { + return arg + 2; + } + + public static long nativeLongPlus(long arg1, long arg2) { + return arg1 + arg2; + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java new file mode 100644 index 000000000000..e1c48bb85563 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestWholeClassStub; + +import java.util.function.Supplier; + +@HostSideTestWholeClassStub +public class TinyFrameworkNestedClasses { + public final Supplier<Integer> mSupplier = new Supplier<Integer>() { + @Override + public Integer get() { + return 1; + } + }; + + public static final Supplier<Integer> sSupplier = new Supplier<Integer>() { + @Override + public Integer get() { + return 2; + } + }; + public Supplier<Integer> getSupplier() { + return new Supplier<Integer>() { + @Override + public Integer get() { + return 3; + } + }; + } + + public static Supplier<Integer> getSupplier_static() { + return new Supplier<Integer>() { + @Override + public Integer get() { + return 4; + } + }; + } + + @HostSideTestWholeClassStub + public class InnerClass { + public int value = 5; + } + + @HostSideTestWholeClassStub + public static class StaticNestedClass { + public int value = 6; + + // Double-nest + public static Supplier<Integer> getSupplier_static() { + return new Supplier<Integer>() { + @Override + public Integer get() { + return 7; + } + }; + } + } + + public static class BaseClass { + public int value; + public BaseClass(int x) { + value = x; + } + } + + public static class SubClass extends BaseClass { + public SubClass(int x) { + super(x); + } + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java new file mode 100644 index 000000000000..6b5110ef2cef --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import org.junit.Test; + +import java.text.DecimalFormat; + +/** + * Contains simple micro-benchmarks. + */ +public class TinyFrameworkBenchmark { + private static final int MINIMAL_ITERATION = 1000; + private static final int MEASURE_SECONDS = 3; + + private static final DecimalFormat sFormatter = new DecimalFormat("#,###"); + + private void doBenchmark(String name, Runnable r) { + // Worm up + for (int i = 0; i < MINIMAL_ITERATION; i++) { + r.run(); + } + + // Start measuring. + final long start = System.nanoTime(); + final long end = start + MEASURE_SECONDS * 1_000_000_000L; + + double iteration = 0; + while (System.nanoTime() <= end) { + for (int i = 0; i < MINIMAL_ITERATION; i++) { + r.run(); + } + iteration += MINIMAL_ITERATION; + } + + final long realEnd = System.nanoTime(); + + System.out.println(String.format("%s\t%s", name, + sFormatter.format((((double) realEnd - start)) / iteration))); + } + + /** + * Micro-benchmark for a method without a non-stub caller check. + */ + @Test + public void benchNoCallerCheck() { + doBenchmark("No caller check", TinyFrameworkCallerCheck::getOne_noCheck); + } + + /** + * Micro-benchmark for a method with a non-stub caller check. + */ + @Test + public void benchWithCallerCheck() { + doBenchmark("With caller check", TinyFrameworkCallerCheck::getOne_withCheck); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java new file mode 100644 index 000000000000..246d06526f5b --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import static com.google.common.truth.Truth.assertThat; + +import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class TinyFrameworkClassTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testSimple() { + TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy(); + assertThat(tfc.addOne(1)).isEqualTo(2); + assertThat(tfc.stub).isEqualTo(1); + } + +// @Test +// public void testDoesntCompile() { +// TinyFrameworkClass tfc = new TinyFrameworkClass(); +// +// tfc.addOneInner(1); // Shouldn't compile. +// tfc.toBeRemoved("abc"); // Shouldn't compile. +// tfc.unsupportedMethod(); // Shouldn't compile. +// int a = tfc.keep; // Shouldn't compile +// int b = tfc.remove; // Shouldn't compile +// } + + @Test + public void testSubstitute() { + TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy(); + assertThat(tfc.addTwo(1)).isEqualTo(3); + } + + @Test + public void testSubstituteNative() { + TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy(); + assertThat(tfc.nativeAddThree(1)).isEqualTo(4); + } + + @Test + public void testVisibleButUsesUnsupportedMethod() { + TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy(); + + thrown.expect(RuntimeException.class); + thrown.expectMessage("This method is not supported on the host side"); + tfc.visibleButUsesUnsupportedMethod(); + } + + @Test + public void testNestedClass1() { + assertThat(new TinyFrameworkNestedClasses().mSupplier.get()).isEqualTo(1); + } + + @Test + public void testNestedClass2() { + assertThat(TinyFrameworkNestedClasses.sSupplier.get()).isEqualTo(2); + } + + @Test + public void testNestedClass3() { + assertThat(new TinyFrameworkNestedClasses().getSupplier().get()).isEqualTo(3); + } + + @Test + public void testNestedClass4() { + assertThat(TinyFrameworkNestedClasses.getSupplier_static().get()).isEqualTo(4); + } + + @Test + public void testNestedClass5() { + assertThat((new TinyFrameworkNestedClasses()).new InnerClass().value).isEqualTo(5); + } + + @Test + public void testNestedClass6() { + assertThat(new TinyFrameworkNestedClasses.StaticNestedClass().value).isEqualTo(6); + } + + @Test + public void testNestedClass7() { + assertThat(TinyFrameworkNestedClasses.StaticNestedClass.getSupplier_static().get()) + .isEqualTo(7); + } + + @Test + public void testNativeSubstitutionClass() { + assertThat(TinyFrameworkNative.nativeAddTwo(3)).isEqualTo(5); + } + + @Test + public void testExitLog() { + thrown.expect(RuntimeException.class); + thrown.expectMessage("Outer exception"); + + TinyFrameworkExceptionTester.testException(); + + } + + @Test + public void testMethodCallBeforeSuperCall() { + assertThat(new SubClass(3).value).isEqualTo(3); + } + + @Test + public void testClassLoadHook() { + assertThat(TinyFrameworkClassWithInitializer.sInitialized).isTrue(); + + // Having this line before assertThat() will ensure these class are already loaded. + var classes = new Class[] { + TinyFrameworkClassWithInitializer.class, + TinyFrameworkClassAnnotations.class, + TinyFrameworkForTextPolicy.class, + }; + + // The following classes have a class load hook, so they should be registered. + assertThat(TinyFrameworkClassLoadHook.sLoadedClasses) + .containsAnyIn(classes); + + // This class doesn't have a class load hook, so shouldn't be included. + assertThat(TinyFrameworkClassLoadHook.sLoadedClasses) + .doesNotContain(TinyFrameworkNestedClasses.class); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java new file mode 100644 index 000000000000..20cc2ec9d50d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.test.tinyframework; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class TinyFrameworkClassWithAnnotTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testSimple() { + TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + assertThat(tfc.addOne(1)).isEqualTo(2); + assertThat(tfc.stub).isEqualTo(1); + } + +// @Test +// public void testDoesntCompile() { +// TinyFrameworkClassWithAnnot tfc = new TinyFrameworkClassWithAnnot(); +// +// tfc.addOneInner(1); // Shouldn't compile. +// tfc.toBeRemoved("abc"); // Shouldn't compile. +// tfc.unsupportedMethod(); // Shouldn't compile. +// int a = tfc.keep; // Shouldn't compile +// int b = tfc.remove; // Shouldn't compile +// } + + @Test + public void testSubstitute() { + TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + assertThat(tfc.addTwo(1)).isEqualTo(3); + } + + @Test + public void testSubstituteNative() { + TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + assertThat(tfc.nativeAddThree(1)).isEqualTo(4); + } + + @Test + public void testVisibleButUsesUnsupportedMethod() { + TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + + thrown.expect(RuntimeException.class); + thrown.expectMessage("This method is not supported on the host side"); + tfc.visibleButUsesUnsupportedMethod(); + } +} diff --git a/tools/hoststubgen/scripts/Android.bp b/tools/hoststubgen/scripts/Android.bp new file mode 100644 index 000000000000..5da805e5640e --- /dev/null +++ b/tools/hoststubgen/scripts/Android.bp @@ -0,0 +1,26 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +sh_binary_host { + name: "dump-jar", + src: "dump-jar", + visibility: ["//visibility:public"], +} + +genrule_defaults { + name: "hoststubgen-jar-dump-defaults", + tools: ["dump-jar"], + cmd: "$(location dump-jar) -s -o $(out) $(in)", +} + +sh_binary_host { + name: "run-ravenwood-test", + src: "run-ravenwood-test", + visibility: ["//visibility:public"], +} diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh b/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh new file mode 100755 index 000000000000..72681234dad8 --- /dev/null +++ b/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Script to build `framework-host-stub` and `framework-host-impl`, and copy the +# generated jars to $out, and unzip them. Useful for looking into the generated files. + +source "${0%/*}"/../common.sh + +out=framework-all-stub-out + +rm -fr $out +mkdir -p $out + +# Build the jars with `m`. +run m framework-all-hidden-api-host + +# Copy the jar to out/ and extract them. +run cp \ + $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/framework-all-hidden-api-host/linux_glibc_common/gen/* \ + $out + +extract $out/*.jar diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh b/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh new file mode 100755 index 000000000000..c3605a9ffaa5 --- /dev/null +++ b/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Script to build hoststubgen and run it directly (without using the build rules) +# on framework-all.jar. + + +echo "THIS SCRIPT IS BROKEN DUE TO CHANGES TO FILE PATHS TO DEPENDENT FILES. FIX IT WHEN YOU NEED TO USE IT." 1>&2 + +exit 99 + + +source "${0%/*}"/../common.sh + +out=out + +mkdir -p $out + +# Build the tool and target jar. +run m hoststubgen framework-all + +base_args=( + @../hoststubgen/hoststubgen-standard-options.txt + + --in-jar $ANDROID_BUILD_TOP/out/soong/.intermediates/frameworks/base/framework-all/android_common/combined/framework-all.jar + --policy-override-file ../hoststubgen/framework-policy-override.txt "${@}" + + # This file will contain all classes as an annotation file, with "keep all" policy. + --gen-keep-all-file $out/framework-all-keep-all-policy.txt + + # This file will contains dump of all classes in the input jar. + --gen-input-dump-file $out/framework-all-dump.txt +) + +do_it() { + local out_file_stem="$1" + shift + local extra_args=("${@}") + + run hoststubgen \ + "${base_args[@]}" \ + "${extra_args[@]}" \ + --out-stub-jar ${out_file_stem}_stub.jar \ + --out-impl-jar ${out_file_stem}_impl.jar \ + $HOSTSTUBGEN_OPTS + + # Extract the jar files, so we can look into them. + run extract ${out_file_stem}_*.jar +} + +#----------------------------------------------------------------------------- +# framework-all, with all hidden APIs. +#----------------------------------------------------------------------------- + +# No extra args. +do_it $out/framework-all_host + +#----------------------------------------------------------------------------- +# framework-test-api, only public/system/test-APIs in the stub. +#----------------------------------------------------------------------------- + +do_it $out/framework-test-api_host \ + --intersect-stub-jar $SOONG_INT/frameworks/base/api/android_test_stubs_current/android_common/combined/*.jar diff --git a/tools/hoststubgen/scripts/dump-jar b/tools/hoststubgen/scripts/dump-jar new file mode 100755 index 000000000000..93729fb22caa --- /dev/null +++ b/tools/hoststubgen/scripts/dump-jar @@ -0,0 +1,163 @@ +#!/bin/bash +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + + +help() { + cat <<'EOF' + + dump-jar: Dump java classes in jar files + + Usage: + dump-jar [-v] CLASS-FILE [...] + + Dump a *.class file + + dump-jar [-v] [-s] [-o OUTPUT-FILENAME] JAR-FILE[: filename regex] [...] + + Dump a jar file. + + If a filename contains a ':', then the following part + will be used to filter files in the jar file. + + For example, "file.jar:/MyClass$" will only dump "MyClass" in file.jar. + + Options: + -v: Enable verbose output. + + -s: Simple output mode, used to check HostStubGen output jars. + + -o: Write the output to a specified file. +EOF +} + +# Parse the options. + +verbose=0 +simple=0 +output="" +while getopts "hvso:" opt; do +case "$opt" in + h) + help + exit 0 + ;; + v) + verbose=1 + ;; + s) + simple=1 + ;; + o) + output="$OPTARG" + ;; + '?') + help + exit 1 + ;; +esac +done +shift $(($OPTIND - 1)) + +JAVAP_OPTS="${JAVAP_OPTS:--v -p -s -sysinfo -constants}" + +if (( $simple )) ; then + JAVAP_OPTS="-p -c -v" +fi + + +# Normalize a java class name. +# Convert '.' to '/' +# Remove the *.class suffix. +normalize() { + local name="$1" + name="${name%.class}" # Remove the .class suffix. + echo "$name" | tr '.' '/' +} + +# Convert the output for `-s` as needed. +filter_output() { + if (( $simple )) ; then + # For "simple output" mode, + # - Normalize the constant numbers (replace with "#x") + # - Remove the constant pool + # - Remove the line number table + # - Some other transient lines + # + # `/PATTERN-1/,/PATTERN-1/{//!d}` is a trick to delete lines between two patterns, without + # the start and the end lines. + sed -e 's/#[0-9][0-9]*/#x/g' \ + -e '/^Constant pool:/,/^[^ ]/{//!d}' \ + -e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \ + -e '/SHA-256 checksum/d' \ + -e '/Last modified/d' \ + -e '/^Classfile jar/d' + else + cat # Print as-is. + fi +} + +# Write to the output file (specified with -o) as needed. +write_to_out() { + if [[ -n "$output" ]] ; then + cat >"$output" + echo "Wrote output to $output" 1>&2 + else + cat # print to stdout + fi +} + +for file in "${@}"; do + + # *.class? + if echo "$file" | grep -qE '\.class$' ; then + echo "# Class: $file" 1>&2 + javap $dump_code_opt $JAVAP_OPTS $file + + # *.jar? + elif echo "$file" | grep -qE '\.jar(:.*)?$' ; then + # Take the regex. Remove everything up to : in $file + regex="" + if [[ "$file" =~ : ]] ; then + regex="$(normalize "${file##*:}")" + fi + + # Remove everything after ':', inclusively, in $file. + file="${file%:*}" + + # Print the filename and the regex. + if ! (( $simple )) ; then + echo -n "# Jar: $file" + if [[ "$regex" != "" ]] ;then + echo -n " (regex: $regex)" + fi + echo + fi + + jar tf "$file" | grep '\.class$' | sort | while read -r class ; do + if normalize "$class" | grep -q -- "$regex" ; then + echo "## Class: $class" + javap $dump_code_opt $JAVAP_OPTS -cp $file ${class%.class} + else + (( $verbose )) && echo "## Skipping class: $class" + fi + done + + else + echo "Unknown file type: $file" 1>&2 + exit 1 + fi +done | filter_output | write_to_out diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh new file mode 100755 index 000000000000..6bc0ddb1a8dc --- /dev/null +++ b/tools/hoststubgen/scripts/run-all-tests.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source "${0%/*}"/../common.sh + +# Move to the top directory of hoststubgen +cd .. + +# These tests are known to pass. +READY_TEST_MODULES=( + HostStubGenTest-framework-all-test-host-test + hoststubgen-test-tiny-test +) + +# First, build all the test modules. This shouldn't fail. +run m run-ravenwood-test ${READY_TEST_MODULES[*]} ${NOT_READY_TEST_MODULES[*]} + +# Next, run the golden check. This should always pass too. +# The following scripts _should_ pass too, but they depend on the internal paths to soong generated +# files, and they may fail when something changes in the build system. +run ./hoststubgen/test-tiny-framework/diff-and-update-golden.sh + +run ./hoststubgen/test-framework/run-test-without-atest.sh +run ./hoststubgen/test-tiny-framework/run-test-manually.sh +run ./scripts/build-framework-hostside-jars-and-extract.sh + +# This script is already broken on goog/master +# run ./scripts/build-framework-hostside-jars-without-genrules.sh + +# These tests should all pass. +run-ravenwood-test ${READY_TEST_MODULES[*]} + +echo ""${0##*/}" finished, with no unexpected failures. Ready to submit!"
\ No newline at end of file diff --git a/tools/hoststubgen/scripts/run-ravenwood-test b/tools/hoststubgen/scripts/run-ravenwood-test new file mode 100755 index 000000000000..9bbb859f5c3d --- /dev/null +++ b/tools/hoststubgen/scripts/run-ravenwood-test @@ -0,0 +1,129 @@ +#!/bin/bash +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +# Script to run a "Ravenwood" host side test. +# +# A proper way to run these tests is to use `atest`, but `atest` has a known issue of loading +# unrelated jar files as the class path, so for now we use this script to run host side tests. + +# Copy (with some changes) some functions from ../common.sh, so this script can be used without it. + +m() { + if (( $SKIP_BUILD )) ; then + echo "Skipping build: $*" 1>&2 + return 0 + fi + run ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@" +} + +run() { + echo "Running: $*" 1>&2 + "$@" +} + +run_junit_test_jar() { + local jar="$1" + echo "Starting test: $jar ..." + run cd "${jar%/*}" + + run ${JAVA:-java} $JAVA_OPTS \ + -cp $jar \ + org.junit.runner.JUnitCore \ + com.android.hoststubgen.hosthelper.HostTestSuite || return 1 + return 0 +} + +help() { + cat <<'EOF' + + run-ravenwood-test -- Run Ravenwood host tests + + Usage: + run-ravenwood-test [options] MODULE-NAME ... + + Options: + -h: Help + -t: Run test only, without building + -b: Build only, without running + + Example: + run-ravenwood-test HostStubGenTest-framework-test-host-test + +EOF +} + +#------------------------------------------------------------------------- +# Parse options +#------------------------------------------------------------------------- +build=0 +test=0 + +while getopts "htb" opt; do + case "$opt" in + h) help; exit 0 ;; + t) + test=1 + ;; + b) + build=1 + ;; + esac +done +shift $(($OPTIND - 1)) + +# If neither -t nor -b is provided, then build and run./ +if (( ( $build + $test ) == 0 )) ; then + build=1 + test=1 +fi + + +modules=("${@}") + +if (( "${#modules[@]}" == 0 )); then + help + exit 1 +fi + +#------------------------------------------------------------------------- +# Build +#------------------------------------------------------------------------- +if (( $build )) ; then + run m "${modules[@]}" +fi + +#------------------------------------------------------------------------- +# Run +#------------------------------------------------------------------------- + +failures=0 +if (( test )) ; then + for module in "${modules[@]}"; do + if run_junit_test_jar "$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/${module}/${module}.jar"; then + : # passed. + else + failures=$(( failures + 1 )) + fi + done + + if (( $failures > 0 )) ; then + echo "$failures test jar(s) failed." 1>&2 + exit 2 + fi +fi + +exit 0
\ No newline at end of file diff --git a/tools/split-select/Main.cpp b/tools/split-select/Main.cpp index 1e751171b370..73bfa19a051a 100644 --- a/tools/split-select/Main.cpp +++ b/tools/split-select/Main.cpp @@ -257,7 +257,7 @@ static int main(int argc, char** argv) { usage(); return 1; } - targetConfigStr.setTo(*argv); + targetConfigStr = *argv; } else if (arg == "--split") { argc--; argv++; @@ -281,7 +281,7 @@ static int main(int argc, char** argv) { usage(); return 1; } - baseApkPath.setTo(*argv); + baseApkPath = *argv; } else if (arg == "--generate") { generateFlag = true; } else if (arg == "--help") { diff --git a/tools/split-select/SplitDescription.cpp b/tools/split-select/SplitDescription.cpp index 71500080193f..c02e2d797608 100644 --- a/tools/split-select/SplitDescription.cpp +++ b/tools/split-select/SplitDescription.cpp @@ -134,10 +134,10 @@ bool SplitDescription::parse(const String8& str, SplitDescription* outSplit) { String8 configStr; String8 extensionStr; if (index >= 0) { - configStr.setTo(str.c_str(), index); - extensionStr.setTo(str.c_str() + index + 1); + configStr = String8(str.c_str(), index); + extensionStr = (str.c_str() + index + 1); } else { - configStr.setTo(str); + configStr = str; } SplitDescription split; |