diff -Nru android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/Android.mk android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/Android.mk --- android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/Android.mk 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -# 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. - -# Java method trace dump tool - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_CPP_EXTENSION := cc -LOCAL_SRC_FILES := tracedump.cc -LOCAL_CFLAGS += -O0 -g -Wall -LOCAL_MODULE_HOST_OS := darwin linux windows -LOCAL_MODULE := dmtracedump -include $(BUILD_HOST_EXECUTABLE) - -include $(CLEAR_VARS) -LOCAL_CPP_EXTENSION := cc -LOCAL_SRC_FILES := createtesttrace.cc -LOCAL_CFLAGS += -O0 -g -Wall -LOCAL_MODULE := create_test_dmtrace -include $(BUILD_HOST_EXECUTABLE) diff -Nru android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/createtesttrace.cc android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/createtesttrace.cc --- android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/createtesttrace.cc 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/createtesttrace.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,449 +0,0 @@ -/* - * Copyright 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. - */ - -/* - * Create a test file in the format required by dmtrace. - */ -#include "profile.h" // from VM header - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Values from the header of the data file. - */ -typedef struct DataHeader { - uint32_t magic; - int16_t version; - int16_t offsetToData; - int64_t startWhen; -} DataHeader; - -#define VERSION 2 -int32_t versionNumber = VERSION; -int32_t verbose = 0; - -DataHeader header = {0x574f4c53, VERSION, sizeof(DataHeader), 0LL}; - -const char* versionHeader = "*version\n"; -const char* clockDef = "clock=thread-cpu\n"; - -const char* keyThreads = - "*threads\n" - "1 main\n" - "2 foo\n" - "3 bar\n" - "4 blah\n"; - -const char* keyEnd = "*end\n"; - -typedef struct dataRecord { - uint32_t time; - int32_t threadId; - uint32_t action; /* 0=entry, 1=exit, 2=exception exit */ - char* fullName; - char* className; - char* methodName; - char* signature; - uint32_t methodId; -} dataRecord; - -dataRecord* records; - -#define BUF_SIZE 1024 -char buf[BUF_SIZE]; - -typedef struct stack { - dataRecord** frames; - int32_t indentLevel; -} stack; - -/* Mac OS doesn't have strndup(), so implement it here. - */ -char* strndup(const char* src, size_t len) { - char* dest = new char[len + 1]; - strncpy(dest, src, len); - dest[len] = 0; - return dest; -} - -/* - * Parse the input file. It looks something like this: - * # This is a comment line - * 4 1 A - * 6 1 B - * 8 1 B - * 10 1 A - * - * where the first column is the time, the second column is the thread id, - * and the third column is the method (actually just the class name). The - * number of spaces between the 2nd and 3rd columns is the indentation and - * determines the call stack. Each called method must be indented by one - * more space. In the example above, A is called at time 4, A calls B at - * time 6, B returns at time 8, and A returns at time 10. Thread 1 is the - * only thread that is running. - * - * An alternative file format leaves out the first two columns: - * A - * B - * B - * A - * - * In this file format, the thread id is always 1, and the time starts at - * 2 and increments by 2 for each line. - */ -void parseInputFile(const char* inputFileName) { - FILE* inputFp = fopen(inputFileName, "r"); - if (inputFp == nullptr) { - perror(inputFileName); - exit(1); - } - - /* Count the number of lines in the buffer */ - int32_t numRecords = 0; - int32_t maxThreadId = 1; - int32_t maxFrames = 0; - char* indentEnd; - while (fgets(buf, BUF_SIZE, inputFp)) { - char* cp = buf; - if (*cp == '#') continue; - numRecords += 1; - if (isdigit(*cp)) { - while (isspace(*cp)) cp += 1; - int32_t threadId = strtoul(cp, &cp, 0); - if (maxThreadId < threadId) maxThreadId = threadId; - } - indentEnd = cp; - while (isspace(*indentEnd)) indentEnd += 1; - if (indentEnd - cp + 1 > maxFrames) maxFrames = indentEnd - cp + 1; - } - int32_t numThreads = maxThreadId + 1; - - /* Add space for a sentinel record at the end */ - numRecords += 1; - records = new dataRecord[numRecords]; - stack* callStack = new stack[numThreads]; - for (int32_t ii = 0; ii < numThreads; ++ii) { - callStack[ii].frames = nullptr; - callStack[ii].indentLevel = 0; - } - - rewind(inputFp); - - uint32_t time = 0; - int32_t linenum = 0; - int32_t nextRecord = 0; - int32_t indentLevel = 0; - while (fgets(buf, BUF_SIZE, inputFp)) { - uint32_t threadId; - int32_t len; - int32_t indent; - int32_t action; - char* save_cp; - - linenum += 1; - char* cp = buf; - - /* Skip lines that start with '#' */ - if (*cp == '#') continue; - - /* Get time and thread id */ - if (!isdigit(*cp)) { - /* If the line does not begin with a digit, then fill in - * default values for the time and threadId. - */ - time += 2; - threadId = 1; - } else { - time = strtoul(cp, &cp, 0); - while (isspace(*cp)) cp += 1; - threadId = strtoul(cp, &cp, 0); - cp += 1; - } - - // Allocate space for the thread stack, if necessary - if (callStack[threadId].frames == nullptr) { - dataRecord** stk = new dataRecord*[maxFrames]; - callStack[threadId].frames = stk; - } - indentLevel = callStack[threadId].indentLevel; - - save_cp = cp; - while (isspace(*cp)) { - cp += 1; - } - indent = cp - save_cp + 1; - records[nextRecord].time = time; - records[nextRecord].threadId = threadId; - - save_cp = cp; - while (*cp != '\n') cp += 1; - - /* Remove trailing spaces */ - cp -= 1; - while (isspace(*cp)) cp -= 1; - cp += 1; - len = cp - save_cp; - records[nextRecord].fullName = strndup(save_cp, len); - - /* Parse the name to support "class.method signature" */ - records[nextRecord].className = nullptr; - records[nextRecord].methodName = nullptr; - records[nextRecord].signature = nullptr; - cp = strchr(save_cp, '.'); - if (cp) { - len = cp - save_cp; - if (len > 0) records[nextRecord].className = strndup(save_cp, len); - save_cp = cp + 1; - cp = strchr(save_cp, ' '); - if (cp == nullptr) cp = strchr(save_cp, '\n'); - if (cp && cp > save_cp) { - len = cp - save_cp; - records[nextRecord].methodName = strndup(save_cp, len); - save_cp = cp + 1; - cp = strchr(save_cp, ' '); - if (cp == nullptr) cp = strchr(save_cp, '\n'); - if (cp && cp > save_cp) { - len = cp - save_cp; - records[nextRecord].signature = strndup(save_cp, len); - } - } - } - - if (verbose) { - printf("Indent: %d; IndentLevel: %d; Line: %s", indent, indentLevel, buf); - } - - action = 0; - if (indent == indentLevel + 1) { // Entering a method - if (verbose) printf(" Entering %s\n", records[nextRecord].fullName); - callStack[threadId].frames[indentLevel] = &records[nextRecord]; - } else if (indent == indentLevel) { // Exiting a method - // Exiting method must be currently on top of stack (unless stack is - // empty) - if (callStack[threadId].frames[indentLevel - 1] == nullptr) { - if (verbose) - printf(" Exiting %s (past bottom of stack)\n", - records[nextRecord].fullName); - callStack[threadId].frames[indentLevel - 1] = &records[nextRecord]; - action = 1; - } else { - if (indentLevel < 1) { - fprintf(stderr, "Error: line %d: %s", linenum, buf); - fprintf(stderr, " expected positive (>0) indentation, found %d\n", - indent); - exit(1); - } - char* name = callStack[threadId].frames[indentLevel - 1]->fullName; - if (strcmp(name, records[nextRecord].fullName) == 0) { - if (verbose) printf(" Exiting %s\n", name); - action = 1; - } else { // exiting method doesn't match stack's top method - fprintf(stderr, "Error: line %d: %s", linenum, buf); - fprintf(stderr, " expected exit from %s\n", - callStack[threadId].frames[indentLevel - 1]->fullName); - exit(1); - } - } - } else { - if (nextRecord != 0) { - fprintf(stderr, "Error: line %d: %s", linenum, buf); - fprintf(stderr, " expected indentation %d [+1], found %d\n", - indentLevel, indent); - exit(1); - } - - if (verbose) { - printf(" Nonzero indent at first record\n"); - printf(" Entering %s\n", records[nextRecord].fullName); - } - - // This is the first line of data, so we allow a larger - // initial indent. This allows us to test popping off more - // frames than we entered. - indentLevel = indent - 1; - callStack[threadId].frames[indentLevel] = &records[nextRecord]; - } - - if (action == 0) - indentLevel += 1; - else - indentLevel -= 1; - records[nextRecord].action = action; - callStack[threadId].indentLevel = indentLevel; - - nextRecord += 1; - } - - /* Mark the last record with a sentinel */ - memset(&records[nextRecord], 0, sizeof(dataRecord)); -} - -/* - * Write values to the binary data file. - */ -void write2LE(FILE* fp, uint16_t val) { - putc(val & 0xff, fp); - putc(val >> 8, fp); -} - -void write4LE(FILE* fp, uint32_t val) { - putc(val & 0xff, fp); - putc((val >> 8) & 0xff, fp); - putc((val >> 16) & 0xff, fp); - putc((val >> 24) & 0xff, fp); -} - -void write8LE(FILE* fp, uint64_t val) { - putc(val & 0xff, fp); - putc((val >> 8) & 0xff, fp); - putc((val >> 16) & 0xff, fp); - putc((val >> 24) & 0xff, fp); - putc((val >> 32) & 0xff, fp); - putc((val >> 40) & 0xff, fp); - putc((val >> 48) & 0xff, fp); - putc((val >> 56) & 0xff, fp); -} - -void writeDataRecord(FILE* dataFp, int32_t threadId, uint32_t methodVal, uint32_t elapsedTime) { - if (versionNumber == 1) - putc(threadId, dataFp); - else - write2LE(dataFp, threadId); - write4LE(dataFp, methodVal); - write4LE(dataFp, elapsedTime); -} - -void writeDataHeader(FILE* dataFp) { - struct timeval tv; - struct timezone tz; - - gettimeofday(&tv, &tz); - uint64_t startTime = tv.tv_sec; - startTime = (startTime << 32) | tv.tv_usec; - header.version = versionNumber; - write4LE(dataFp, header.magic); - write2LE(dataFp, header.version); - write2LE(dataFp, header.offsetToData); - write8LE(dataFp, startTime); -} - -void writeKeyMethods(FILE* keyFp) { - const char* methodStr = "*methods\n"; - fwrite(methodStr, strlen(methodStr), 1, keyFp); - - /* Assign method ids in multiples of 4 */ - uint32_t methodId = 0; - for (dataRecord* pRecord = records; pRecord->fullName; ++pRecord) { - if (pRecord->methodId) continue; - uint32_t id = ++methodId << 2; - pRecord->methodId = id; - - /* Assign this id to all the other records that have the - * same name. - */ - for (dataRecord* pNext = pRecord + 1; pNext->fullName; ++pNext) { - if (pNext->methodId) continue; - if (strcmp(pRecord->fullName, pNext->fullName) == 0) pNext->methodId = id; - } - if (pRecord->className == nullptr || pRecord->methodName == nullptr) { - fprintf(keyFp, "%#x %s m ()\n", pRecord->methodId, - pRecord->fullName); - } else if (pRecord->signature == nullptr) { - fprintf(keyFp, "%#x %s %s ()\n", pRecord->methodId, - pRecord->className, pRecord->methodName); - } else { - fprintf(keyFp, "%#x %s %s %s\n", pRecord->methodId, - pRecord->className, pRecord->methodName, pRecord->signature); - } - } -} - -void writeKeys(FILE* keyFp) { - fprintf(keyFp, "%s%d\n%s", versionHeader, versionNumber, clockDef); - fwrite(keyThreads, strlen(keyThreads), 1, keyFp); - writeKeyMethods(keyFp); - fwrite(keyEnd, strlen(keyEnd), 1, keyFp); -} - -void writeDataRecords(FILE* dataFp) { - for (dataRecord* pRecord = records; pRecord->fullName; ++pRecord) { - uint32_t val = METHOD_COMBINE(pRecord->methodId, pRecord->action); - writeDataRecord(dataFp, pRecord->threadId, val, pRecord->time); - } -} - -void writeTrace(const char* traceFileName) { - FILE* fp = fopen(traceFileName, "w"); - if (fp == nullptr) { - perror(traceFileName); - exit(1); - } - writeKeys(fp); - writeDataHeader(fp); - writeDataRecords(fp); - fclose(fp); -} - -int32_t parseOptions(int32_t argc, char** argv) { - int32_t err = 0; - while (1) { - int32_t opt = getopt(argc, argv, "v:d"); - if (opt == -1) break; - switch (opt) { - case 'v': - versionNumber = strtoul(optarg, nullptr, 0); - if (versionNumber != 1 && versionNumber != 2) { - fprintf(stderr, "Error: version number (%d) must be 1 or 2\n", versionNumber); - err = 1; - } - break; - case 'd': - verbose = 1; - break; - default: - err = 1; - break; - } - } - return err; -} - -int32_t main(int32_t argc, char** argv) { - char* inputFile; - char* traceFileName = nullptr; - - if (parseOptions(argc, argv) || argc - optind != 2) { - fprintf(stderr, "Usage: %s [-v version] [-d] input_file trace_prefix\n", argv[0]); - exit(1); - } - - inputFile = argv[optind++]; - parseInputFile(inputFile); - traceFileName = argv[optind++]; - - writeTrace(traceFileName); - - return 0; -} diff -Nru android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/dmtracedump.pl android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/dmtracedump.pl --- android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/dmtracedump.pl 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/dmtracedump.pl 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -#!/usr/bin/perl - -opendir(DIR, ".") || die "can't opendir $some_dir: $!"; -@traces = grep { /.*\.dmtrace\.data/ } readdir(DIR); - -foreach (@traces) -{ - $input = $_; - $input =~ s/\.data$//; - - $output = "$input.html"; - - print("dmtracedump -h -p $input > $output\n"); - system("dmtracedump -h -p '$input' > '$output'"); - -} - -closedir DIR; diff -Nru android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/dumpdir.sh android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/dumpdir.sh --- android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/dumpdir.sh 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/dumpdir.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -#!/bin/bash - -FILES=`ls $1/*.data | sed "s/^\\(.*\\).data$/\\1/"` - -mkdir -p $2 - -for F in $FILES -do - G=$2/`echo $F | sed "s/.*\\///g"`.html - dmtracedump -h -p $F > $G -done diff -Nru android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/profile.h android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/profile.h --- android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/profile.h 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/profile.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2008 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. - */ - -/* - * Android's method call profiling goodies. - */ -#ifndef ART_TOOLS_DMTRACEDUMP_PROFILE_H_ -#define ART_TOOLS_DMTRACEDUMP_PROFILE_H_ - -/* - * Enumeration for the two "action" bits. - */ -enum { - METHOD_TRACE_ENTER = 0x00, // method entry - METHOD_TRACE_EXIT = 0x01, // method exit - METHOD_TRACE_UNROLL = 0x02, // method exited by exception unrolling - // 0x03 currently unused -}; - -#define TOKEN_CHAR '*' - -/* - * Common definitions, shared with the dump tool. - */ -#define METHOD_ACTION_MASK 0x03 /* two bits */ -#define METHOD_ID(_method) ((_method) & (~METHOD_ACTION_MASK)) -#define METHOD_ACTION(_method) (((unsigned int)(_method)) & METHOD_ACTION_MASK) -#define METHOD_COMBINE(_method, _action) ((_method) | (_action)) - -#endif // ART_TOOLS_DMTRACEDUMP_PROFILE_H_ diff -Nru android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/tracedump.cc android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/tracedump.cc --- android-platform-dalvik-7.0.0+r33/debian/additionalSrc/dmtracedump/tracedump.cc 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/additionalSrc/dmtracedump/tracedump.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,2616 +0,0 @@ -/* - * 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. - */ - -/* - * Process dmtrace output. - * - * This is the wrong way to go about it -- C is a clumsy language for - * shuffling data around. It'll do for a first pass. - */ -#include "profile.h" // from VM header - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Version number in the key file. - * Version 1 uses one byte for the thread id. - * Version 2 uses two bytes for the thread ids. - * Version 3 encodes the record size and adds an optional extra timestamp field. - */ -int32_t versionNumber; - -/* arbitrarily limit indentation */ -#define MAX_STACK_DEPTH 10000 - -/* thread list in key file is not reliable, so just max out */ -#define MAX_THREADS 32768 - -/* Size of temporary buffers for escaping html strings */ -#define HTML_BUFSIZE 10240 - -const char* htmlHeader = - "\n\n\n" - "\n" - "\n" - "\n\n"; - -const char* htmlFooter = "\n\n\n"; -const char* profileSeparator = - "======================================================================"; - -const char* tableHeader = - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n"; - -const char* tableHeaderMissing = - "
MethodRun 1 (us)Run 2 (us)Diff (us)Diff (%%)1: # calls2: # calls
\n" - "\n" - "\n" - "\n" - "\n"; - -#define GRAPH_LABEL_VISITED 0x0001 -#define GRAPH_NODE_VISITED 0x0002 - -/* - * Values from the header of the data file. - */ -typedef struct DataHeader { - uint32_t magic; - int16_t version; - int16_t offsetToData; - int64_t startWhen; - int16_t recordSize; -} DataHeader; - -/* - * Entry from the thread list. - */ -typedef struct ThreadEntry { - int32_t threadId; - const char* threadName; -} ThreadEntry; - -struct MethodEntry; -typedef struct TimedMethod { - struct TimedMethod* next; - uint64_t elapsedInclusive; - int32_t numCalls; - struct MethodEntry* method; -} TimedMethod; - -typedef struct ClassEntry { - const char* className; - uint64_t elapsedExclusive; - int32_t numMethods; - struct MethodEntry** methods; /* list of methods in this class */ - int32_t numCalls[2]; /* 0=normal, 1=recursive */ -} ClassEntry; - -typedef struct UniqueMethodEntry { - uint64_t elapsedExclusive; - int32_t numMethods; - struct MethodEntry** methods; /* list of methods with same name */ - int32_t numCalls[2]; /* 0=normal, 1=recursive */ -} UniqueMethodEntry; - -/* - * Entry from the method list. - */ -typedef struct MethodEntry { - int64_t methodId; - const char* className; - const char* methodName; - const char* signature; - const char* fileName; - int32_t lineNum; - uint64_t elapsedExclusive; - uint64_t elapsedInclusive; - uint64_t topExclusive; /* non-recursive exclusive time */ - uint64_t recursiveInclusive; - struct TimedMethod* parents[2]; /* 0=normal, 1=recursive */ - struct TimedMethod* children[2]; /* 0=normal, 1=recursive */ - int32_t numCalls[2]; /* 0=normal, 1=recursive */ - int32_t index; /* used after sorting to number methods */ - int32_t recursiveEntries; /* number of entries on the stack */ - int32_t graphState; /* used when graphing to see if this method has been visited before */ -} MethodEntry; - -/* - * The parsed contents of the key file. - */ -typedef struct DataKeys { - char* fileData; /* contents of the entire file */ - int64_t fileLen; - int32_t numThreads; - ThreadEntry* threads; - int32_t numMethods; - MethodEntry* methods; /* 2 extra methods: "toplevel" and "unknown" */ -} DataKeys; - -#define TOPLEVEL_INDEX 0 -#define UNKNOWN_INDEX 1 - -typedef struct StackEntry { - MethodEntry* method; - uint64_t entryTime; -} StackEntry; - -typedef struct CallStack { - int32_t top; - StackEntry calls[MAX_STACK_DEPTH]; - uint64_t lastEventTime; - uint64_t threadStartTime; -} CallStack; - -typedef struct DiffEntry { - MethodEntry* method1; - MethodEntry* method2; - int64_t differenceExclusive; - int64_t differenceInclusive; - double differenceExclusivePercentage; - double differenceInclusivePercentage; -} DiffEntry; - -// Global options -typedef struct Options { - const char* traceFileName; - const char* diffFileName; - const char* graphFileName; - int32_t keepDotFile; - int32_t dump; - int32_t outputHtml; - const char* sortableUrl; - int32_t threshold; -} Options; - -typedef struct TraceData { - int32_t numClasses; - ClassEntry* classes; - CallStack* stacks[MAX_THREADS]; - int32_t depth[MAX_THREADS]; - int32_t numUniqueMethods; - UniqueMethodEntry* uniqueMethods; -} TraceData; - -static Options gOptions; - -/* Escapes characters in the source string that are html special entities. - * The escaped string is written to "dest" which must be large enough to - * hold the result. A pointer to "dest" is returned. The characters and - * their corresponding escape sequences are: - * '<' < - * '>' > - * '&' & - */ -char* htmlEscape(const char* src, char* dest, int32_t len) { - char* destStart = dest; - - if (src == nullptr) return nullptr; - - int32_t nbytes = 0; - while (*src) { - if (*src == '<') { - nbytes += 4; - if (nbytes >= len) break; - *dest++ = '&'; - *dest++ = 'l'; - *dest++ = 't'; - *dest++ = ';'; - } else if (*src == '>') { - nbytes += 4; - if (nbytes >= len) break; - *dest++ = '&'; - *dest++ = 'g'; - *dest++ = 't'; - *dest++ = ';'; - } else if (*src == '&') { - nbytes += 5; - if (nbytes >= len) break; - *dest++ = '&'; - *dest++ = 'a'; - *dest++ = 'm'; - *dest++ = 'p'; - *dest++ = ';'; - } else { - nbytes += 1; - if (nbytes >= len) break; - *dest++ = *src; - } - src += 1; - } - if (nbytes >= len) { - fprintf(stderr, "htmlEscape(): buffer overflow\n"); - exit(1); - } - *dest = 0; - - return destStart; -} - -/* Initializes a MethodEntry - */ -void initMethodEntry(MethodEntry* method, int64_t methodId, const char* className, - const char* methodName, const char* signature, const char* fileName, - const char* lineNumStr) { - method->methodId = methodId; - method->className = className; - method->methodName = methodName; - method->signature = signature; - method->fileName = fileName; - method->lineNum = (lineNumStr != nullptr) ? atoi(lineNumStr) : -1; - method->elapsedExclusive = 0; - method->elapsedInclusive = 0; - method->topExclusive = 0; - method->recursiveInclusive = 0; - method->parents[0] = nullptr; - method->parents[1] = nullptr; - method->children[0] = nullptr; - method->children[1] = nullptr; - method->numCalls[0] = 0; - method->numCalls[1] = 0; - method->index = 0; - method->recursiveEntries = 0; -} - -/* - * This comparison function is called from qsort() to sort - * methods into decreasing order of exclusive elapsed time. - */ -int32_t compareElapsedExclusive(const void* a, const void* b) { - const MethodEntry* methodA = *(const MethodEntry**) a; - const MethodEntry* methodB = *(const MethodEntry**) b; - uint64_t elapsed1 = methodA->elapsedExclusive; - uint64_t elapsed2 = methodB->elapsedExclusive; - if (elapsed1 < elapsed2) return 1; - if (elapsed1 > elapsed2) return -1; - - /* If the elapsed times of two methods are equal, then sort them - * into alphabetical order. - */ - int32_t result = strcmp(methodA->className, methodB->className); - if (result == 0) { - if (methodA->methodName == nullptr || methodB->methodName == nullptr) { - int64_t idA = methodA->methodId; - int64_t idB = methodB->methodId; - if (idA < idB) return -1; - if (idA > idB) return 1; - return 0; - } - result = strcmp(methodA->methodName, methodB->methodName); - if (result == 0) result = strcmp(methodA->signature, methodB->signature); - } - return result; -} - -/* - * This comparison function is called from qsort() to sort - * methods into decreasing order of inclusive elapsed time. - */ -int32_t compareElapsedInclusive(const void* a, const void* b) { - const MethodEntry* methodA = *(MethodEntry const**) a; - const MethodEntry* methodB = *(MethodEntry const**) b; - uint64_t elapsed1 = methodA->elapsedInclusive; - uint64_t elapsed2 = methodB->elapsedInclusive; - if (elapsed1 < elapsed2) return 1; - if (elapsed1 > elapsed2) return -1; - - /* If the elapsed times of two methods are equal, then sort them - * into alphabetical order. - */ - int32_t result = strcmp(methodA->className, methodB->className); - if (result == 0) { - if (methodA->methodName == nullptr || methodB->methodName == nullptr) { - int64_t idA = methodA->methodId; - int64_t idB = methodB->methodId; - if (idA < idB) return -1; - if (idA > idB) return 1; - return 0; - } - result = strcmp(methodA->methodName, methodB->methodName); - if (result == 0) result = strcmp(methodA->signature, methodB->signature); - } - return result; -} - -/* - * This comparison function is called from qsort() to sort - * TimedMethods into decreasing order of inclusive elapsed time. - */ -int32_t compareTimedMethod(const void* a, const void* b) { - const TimedMethod* timedA = (TimedMethod const*) a; - const TimedMethod* timedB = (TimedMethod const*) b; - uint64_t elapsed1 = timedA->elapsedInclusive; - uint64_t elapsed2 = timedB->elapsedInclusive; - if (elapsed1 < elapsed2) return 1; - if (elapsed1 > elapsed2) return -1; - - /* If the elapsed times of two methods are equal, then sort them - * into alphabetical order. - */ - MethodEntry* methodA = timedA->method; - MethodEntry* methodB = timedB->method; - int32_t result = strcmp(methodA->className, methodB->className); - if (result == 0) { - if (methodA->methodName == nullptr || methodB->methodName == nullptr) { - int64_t idA = methodA->methodId; - int64_t idB = methodB->methodId; - if (idA < idB) return -1; - if (idA > idB) return 1; - return 0; - } - result = strcmp(methodA->methodName, methodB->methodName); - if (result == 0) result = strcmp(methodA->signature, methodB->signature); - } - return result; -} - -/* - * This comparison function is called from qsort() to sort - * MethodEntry pointers into alphabetical order of class names. - */ -int32_t compareClassNames(const void* a, const void* b) { - const MethodEntry* methodA = *(const MethodEntry**) a; - const MethodEntry* methodB = *(const MethodEntry**) b; - int32_t result = strcmp(methodA->className, methodB->className); - if (result == 0) { - int64_t idA = methodA->methodId; - int64_t idB = methodB->methodId; - if (idA < idB) return -1; - if (idA > idB) return 1; - return 0; - } - return result; -} - -/* - * This comparison function is called from qsort() to sort - * classes into decreasing order of exclusive elapsed time. - */ -int32_t compareClassExclusive(const void* a, const void* b) { - const ClassEntry* classA = *(const ClassEntry**) a; - const ClassEntry* classB = *(const ClassEntry**) b; - uint64_t elapsed1 = classA->elapsedExclusive; - uint64_t elapsed2 = classB->elapsedExclusive; - if (elapsed1 < elapsed2) return 1; - if (elapsed1 > elapsed2) return -1; - - /* If the elapsed times of two classs are equal, then sort them - * into alphabetical order. - */ - int32_t result = strcmp(classA->className, classB->className); - if (result == 0) { - /* Break ties with the first method id. This is probably not - * needed. - */ - int64_t idA = classA->methods[0]->methodId; - int64_t idB = classB->methods[0]->methodId; - if (idA < idB) return -1; - if (idA > idB) return 1; - return 0; - } - return result; -} - -/* - * This comparison function is called from qsort() to sort - * MethodEntry pointers into alphabetical order by method name, - * then by class name. - */ -int32_t compareMethodNames(const void* a, const void* b) { - const MethodEntry* methodA = *(const MethodEntry**) a; - const MethodEntry* methodB = *(const MethodEntry**) b; - if (methodA->methodName == nullptr || methodB->methodName == nullptr) { - return compareClassNames(a, b); - } - int32_t result = strcmp(methodA->methodName, methodB->methodName); - if (result == 0) { - result = strcmp(methodA->className, methodB->className); - if (result == 0) { - int64_t idA = methodA->methodId; - int64_t idB = methodB->methodId; - if (idA < idB) return -1; - if (idA > idB) return 1; - return 0; - } - } - return result; -} - -/* - * This comparison function is called from qsort() to sort - * unique methods into decreasing order of exclusive elapsed time. - */ -int32_t compareUniqueExclusive(const void* a, const void* b) { - const UniqueMethodEntry* uniqueA = *(const UniqueMethodEntry**) a; - const UniqueMethodEntry* uniqueB = *(const UniqueMethodEntry**) b; - uint64_t elapsed1 = uniqueA->elapsedExclusive; - uint64_t elapsed2 = uniqueB->elapsedExclusive; - if (elapsed1 < elapsed2) return 1; - if (elapsed1 > elapsed2) return -1; - - /* If the elapsed times of two methods are equal, then sort them - * into alphabetical order. - */ - int32_t result = strcmp(uniqueA->methods[0]->className, uniqueB->methods[0]->className); - if (result == 0) { - int64_t idA = uniqueA->methods[0]->methodId; - int64_t idB = uniqueB->methods[0]->methodId; - if (idA < idB) return -1; - if (idA > idB) return 1; - return 0; - } - return result; -} - -/* - * Free a DataKeys struct. - */ -void freeDataKeys(DataKeys* pKeys) { - if (pKeys == nullptr) return; - - free(pKeys->fileData); - free(pKeys->threads); - free(pKeys->methods); - free(pKeys); -} - -/* - * Find the offset to the next occurrence of the specified character. - * - * "data" should point somewhere within the current line. "len" is the - * number of bytes left in the buffer. - * - * Returns -1 if we hit the end of the buffer. - */ -int32_t findNextChar(const char* data, int32_t len, char lookFor) { - const char* start = data; - - while (len > 0) { - if (*data == lookFor) return data - start; - - data++; - len--; - } - - return -1; -} - -/* - * Count the number of lines until the next token. - * - * Returns -1 if none found before EOF. - */ -int32_t countLinesToToken(const char* data, int32_t len) { - int32_t count = 0; - int32_t next; - - while (*data != TOKEN_CHAR) { - next = findNextChar(data, len, '\n'); - if (next < 0) return -1; - count++; - data += next + 1; - len -= next + 1; - } - - return count; -} - -/* - * Make sure we're at the start of the right section. - * - * Returns the length of the token line, or -1 if something is wrong. - */ -int32_t checkToken(const char* data, int32_t len, const char* cmpStr) { - int32_t cmpLen = strlen(cmpStr); - int32_t next; - - if (*data != TOKEN_CHAR) { - fprintf(stderr, "ERROR: not at start of %s (found '%.10s')\n", cmpStr, data); - return -1; - } - - next = findNextChar(data, len, '\n'); - if (next < cmpLen + 1) return -1; - - if (strncmp(data + 1, cmpStr, cmpLen) != 0) { - fprintf(stderr, "ERROR: '%s' not found (got '%.7s')\n", cmpStr, data + 1); - return -1; - } - - return next + 1; -} - -/* - * Parse the "*version" section. - */ -int64_t parseVersion(DataKeys* pKeys, int64_t offset, int32_t verbose) { - if (offset < 0) return -1; - - char* data = pKeys->fileData + offset; - char* dataEnd = pKeys->fileData + pKeys->fileLen; - int32_t next = checkToken(data, dataEnd - data, "version"); - if (next <= 0) return -1; - - data += next; - - /* - * Count the number of items in the "version" section. - */ - int32_t count = countLinesToToken(data, dataEnd - data); - if (count <= 0) { - fprintf(stderr, "ERROR: failed while reading version (found %d)\n", count); - return -1; - } - - /* find the end of the line */ - next = findNextChar(data, dataEnd - data, '\n'); - if (next < 0) return -1; - - data[next] = '\0'; - versionNumber = strtoul(data, nullptr, 0); - if (verbose) printf("VERSION: %d\n", versionNumber); - - data += next + 1; - - /* skip over the rest of the stuff, which is "name=value" lines */ - for (int32_t i = 1; i < count; i++) { - next = findNextChar(data, dataEnd - data, '\n'); - if (next < 0) return -1; - // data[next] = '\0'; - // printf("IGNORING: '%s'\n", data); - data += next + 1; - } - - return data - pKeys->fileData; -} - -/* - * Parse the "*threads" section. - */ -int64_t parseThreads(DataKeys* pKeys, int64_t offset) { - if (offset < 0) return -1; - - char* data = pKeys->fileData + offset; - char* dataEnd = pKeys->fileData + pKeys->fileLen; - int32_t next = checkToken(data, dataEnd - data, "threads"); - - data += next; - - /* - * Count the number of thread entries (one per line). - */ - int32_t count = countLinesToToken(data, dataEnd - data); - if (count <= 0) { - fprintf(stderr, "ERROR: failed while reading threads (found %d)\n", count); - return -1; - } - - // printf("+++ found %d threads\n", count); - pKeys->threads = new ThreadEntry[count]; - if (pKeys->threads == nullptr) return -1; - - /* - * Extract all entries. - */ - for (int32_t i = 0; i < count; i++) { - next = findNextChar(data, dataEnd - data, '\n'); - assert(next > 0); - data[next] = '\0'; - - int32_t tab = findNextChar(data, next, '\t'); - data[tab] = '\0'; - - pKeys->threads[i].threadId = atoi(data); - pKeys->threads[i].threadName = data + tab + 1; - - data += next + 1; - } - - pKeys->numThreads = count; - return data - pKeys->fileData; -} - -/* - * Parse the "*methods" section. - */ -int64_t parseMethods(DataKeys* pKeys, int64_t offset) { - if (offset < 0) return -1; - - char* data = pKeys->fileData + offset; - char* dataEnd = pKeys->fileData + pKeys->fileLen; - int32_t next = checkToken(data, dataEnd - data, "methods"); - if (next < 0) return -1; - - data += next; - - /* - * Count the number of method entries (one per line). - */ - int32_t count = countLinesToToken(data, dataEnd - data); - if (count <= 0) { - fprintf(stderr, "ERROR: failed while reading methods (found %d)\n", count); - return -1; - } - - /* Reserve an extra method at location 0 for the "toplevel" method, - * and another extra method for all other "unknown" methods. - */ - count += 2; - pKeys->methods = new MethodEntry[count]; - if (pKeys->methods == nullptr) return -1; - initMethodEntry(&pKeys->methods[TOPLEVEL_INDEX], -2, "(toplevel)", nullptr, nullptr, - nullptr, nullptr); - initMethodEntry(&pKeys->methods[UNKNOWN_INDEX], -1, "(unknown)", nullptr, nullptr, - nullptr, nullptr); - - /* - * Extract all entries, starting with index 2. - */ - for (int32_t i = UNKNOWN_INDEX + 1; i < count; i++) { - next = findNextChar(data, dataEnd - data, '\n'); - assert(next > 0); - data[next] = '\0'; - - int32_t tab1 = findNextChar(data, next, '\t'); - int32_t tab2 = findNextChar(data + (tab1 + 1), next - (tab1 + 1), '\t'); - int32_t tab3 = findNextChar(data + (tab1 + tab2 + 2), next - (tab1 + tab2 + 2), '\t'); - int32_t tab4 = findNextChar(data + (tab1 + tab2 + tab3 + 3), - next - (tab1 + tab2 + tab3 + 3), '\t'); - int32_t tab5 = findNextChar(data + (tab1 + tab2 + tab3 + tab4 + 4), - next - (tab1 + tab2 + tab3 + tab4 + 4), '\t'); - if (tab1 < 0) { - fprintf(stderr, "ERROR: missing field on method line: '%s'\n", data); - return -1; - } - assert(data[tab1] == '\t'); - data[tab1] = '\0'; - - char* endptr; - int64_t id = strtoul(data, &endptr, 0); - if (*endptr != '\0') { - fprintf(stderr, "ERROR: bad method ID '%s'\n", data); - return -1; - } - - // Allow files that specify just a function name, instead of requiring - // "class \t method \t signature" - if (tab2 > 0 && tab3 > 0) { - tab2 += tab1 + 1; - tab3 += tab2 + 1; - assert(data[tab2] == '\t'); - assert(data[tab3] == '\t'); - data[tab2] = data[tab3] = '\0'; - - // This is starting to get awkward. Allow filename and line #. - if (tab4 > 0 && tab5 > 0) { - tab4 += tab3 + 1; - tab5 += tab4 + 1; - - assert(data[tab4] == '\t'); - assert(data[tab5] == '\t'); - data[tab4] = data[tab5] = '\0'; - - initMethodEntry(&pKeys->methods[i], id, data + tab1 + 1, - data + tab2 + 1, data + tab3 + 1, data + tab4 + 1, - data + tab5 + 1); - } else { - initMethodEntry(&pKeys->methods[i], id, data + tab1 + 1, - data + tab2 + 1, data + tab3 + 1, nullptr, nullptr); - } - } else { - initMethodEntry(&pKeys->methods[i], id, data + tab1 + 1, nullptr, nullptr, nullptr, - nullptr); - } - - data += next + 1; - } - - pKeys->numMethods = count; - return data - pKeys->fileData; -} - -/* - * Parse the "*end" section. - */ -int64_t parseEnd(DataKeys* pKeys, int64_t offset) { - if (offset < 0) return -1; - - char* data = pKeys->fileData + offset; - char* dataEnd = pKeys->fileData + pKeys->fileLen; - int32_t next = checkToken(data, dataEnd - data, "end"); - if (next < 0) return -1; - - data += next; - - return data - pKeys->fileData; -} - -/* - * Sort the thread list entries. - */ -static int32_t compareThreads(const void* thread1, const void* thread2) { - return ((const ThreadEntry*) thread1)->threadId - - ((const ThreadEntry*) thread2)->threadId; -} - -void sortThreadList(DataKeys* pKeys) { - qsort(pKeys->threads, pKeys->numThreads, sizeof(pKeys->threads[0]), compareThreads); -} - -/* - * Sort the method list entries. - */ -static int32_t compareMethods(const void* meth1, const void* meth2) { - int64_t id1 = ((const MethodEntry*) meth1)->methodId; - int64_t id2 = ((const MethodEntry*) meth2)->methodId; - if (id1 < id2) return -1; - if (id1 > id2) return 1; - return 0; -} - -void sortMethodList(DataKeys* pKeys) { - qsort(pKeys->methods, pKeys->numMethods, sizeof(MethodEntry), compareMethods); -} - -/* - * Parse the key section, and return a copy of the parsed contents. - */ -DataKeys* parseKeys(FILE* fp, int32_t verbose) { - int64_t offset; - DataKeys* pKeys = new DataKeys(); - memset(pKeys, 0, sizeof(DataKeys)); - if (pKeys == nullptr) return nullptr; - - /* - * We load the entire file into memory. We do this, rather than memory- - * mapping it, because we want to change some whitespace to NULs. - */ - if (fseek(fp, 0L, SEEK_END) != 0) { - perror("fseek"); - freeDataKeys(pKeys); - return nullptr; - } - pKeys->fileLen = ftell(fp); - if (pKeys->fileLen == 0) { - fprintf(stderr, "Key file is empty.\n"); - freeDataKeys(pKeys); - return nullptr; - } - rewind(fp); - - pKeys->fileData = new char[pKeys->fileLen]; - if (pKeys->fileData == nullptr) { - fprintf(stderr, "ERROR: unable to alloc %" PRIu64 " bytes\n", pKeys->fileLen); - freeDataKeys(pKeys); - return nullptr; - } - - if (fread(pKeys->fileData, 1, pKeys->fileLen, fp) != (size_t)pKeys->fileLen) { - fprintf(stderr, "ERROR: unable to read %" PRIu64 " bytes from trace file\n", pKeys->fileLen); - freeDataKeys(pKeys); - return nullptr; - } - - offset = 0; - offset = parseVersion(pKeys, offset, verbose); - offset = parseThreads(pKeys, offset); - offset = parseMethods(pKeys, offset); - offset = parseEnd(pKeys, offset); - if (offset < 0) { - freeDataKeys(pKeys); - return nullptr; - } - - /* Reduce our allocation now that we know where the end of the key section is. */ - pKeys->fileData = reinterpret_cast(realloc(pKeys->fileData, offset)); - pKeys->fileLen = offset; - /* Leave fp pointing to the beginning of the data section. */ - fseek(fp, offset, SEEK_SET); - - sortThreadList(pKeys); - sortMethodList(pKeys); - - /* - * Dump list of threads. - */ - if (verbose) { - printf("Threads (%d):\n", pKeys->numThreads); - for (int32_t i = 0; i < pKeys->numThreads; i++) { - printf("%2d %s\n", pKeys->threads[i].threadId, pKeys->threads[i].threadName); - } - } - -#if 0 - /* - * Dump list of methods. - */ - if (verbose) { - printf("Methods (%d):\n", pKeys->numMethods); - for (int32_t i = 0; i < pKeys->numMethods; i++) { - printf("0x%08x %s : %s : %s\n", - pKeys->methods[i].methodId, pKeys->methods[i].className, - pKeys->methods[i].methodName, pKeys->methods[i].signature); - } - } -#endif - - return pKeys; -} - -/* - * Read values from the binary data file. - */ - -/* - * Make the return value "uint32_t" instead of "uint16_t" so that we can detect EOF. - */ -uint32_t read2LE(FILE* fp) { - uint32_t val = getc(fp); - val |= getc(fp) << 8; - return val; -} -uint32_t read4LE(FILE* fp) { - uint32_t val = getc(fp); - val |= getc(fp) << 8; - val |= getc(fp) << 16; - val |= getc(fp) << 24; - return val; -} -uint64_t read8LE(FILE* fp) { - uint64_t val = getc(fp); - val |= (uint64_t) getc(fp) << 8; - val |= (uint64_t) getc(fp) << 16; - val |= (uint64_t) getc(fp) << 24; - val |= (uint64_t) getc(fp) << 32; - val |= (uint64_t) getc(fp) << 40; - val |= (uint64_t) getc(fp) << 48; - val |= (uint64_t) getc(fp) << 56; - return val; -} - -/* - * Parse the header of the data section. - * - * Returns with the file positioned at the start of the record data. - */ -int32_t parseDataHeader(FILE* fp, DataHeader* pHeader) { - pHeader->magic = read4LE(fp); - pHeader->version = read2LE(fp); - pHeader->offsetToData = read2LE(fp); - pHeader->startWhen = read8LE(fp); - int32_t bytesToRead = pHeader->offsetToData - 16; - if (pHeader->version == 1) { - pHeader->recordSize = 9; - } else if (pHeader->version == 2) { - pHeader->recordSize = 10; - } else if (pHeader->version == 3) { - pHeader->recordSize = read2LE(fp); - bytesToRead -= 2; - } else { - fprintf(stderr, "Unsupported trace file version: %d\n", pHeader->version); - return -1; - } - - if (fseek(fp, bytesToRead, SEEK_CUR) != 0) { - return -1; - } - - return 0; -} - -/* - * Look up a method by it's method ID. - * - * Returns nullptr if no matching method was found. - */ -MethodEntry* lookupMethod(DataKeys* pKeys, int64_t methodId) { - int32_t lo = 0; - int32_t hi = pKeys->numMethods - 1; - - while (hi >= lo) { - int32_t mid = (hi + lo) / 2; - - int64_t id = pKeys->methods[mid].methodId; - if (id == methodId) /* match */ - return &pKeys->methods[mid]; - else if (id < methodId) /* too low */ - lo = mid + 1; - else /* too high */ - hi = mid - 1; - } - - return nullptr; -} - -/* - * Reads the next data record, and assigns the data values to threadId, - * methodVal and elapsedTime. On end-of-file, the threadId, methodVal, - * and elapsedTime are unchanged. Returns 1 on end-of-file, otherwise - * returns 0. - */ -int32_t readDataRecord(FILE* dataFp, DataHeader* dataHeader, int32_t* threadId, - uint32_t* methodVal, uint64_t* elapsedTime) { - int32_t id; - int32_t bytesToRead = dataHeader->recordSize; - if (dataHeader->version == 1) { - id = getc(dataFp); - bytesToRead -= 1; - } else { - id = read2LE(dataFp); - bytesToRead -= 2; - } - if (id == EOF) return 1; - *threadId = id; - - *methodVal = read4LE(dataFp); - *elapsedTime = read4LE(dataFp); - bytesToRead -= 8; - - while (bytesToRead-- > 0) { - getc(dataFp); - } - - if (feof(dataFp)) { - fprintf(stderr, "WARNING: hit EOF mid-record\n"); - return 1; - } - return 0; -} - -/* - * Read the key file and use it to produce formatted output from the - * data file. - */ -void dumpTrace() { - static const char* actionStr[] = {"ent", "xit", "unr", "???"}; - MethodEntry bogusMethod = { - 0, "???", "???", "???", "???", -1, 0, 0, - 0, 0, {nullptr, nullptr}, {nullptr, nullptr}, {0, 0}, 0, 0, -1}; - char bogusBuf[80]; - TraceData traceData; - - // printf("Dumping '%s' '%s'\n", dataFileName, keyFileName); - - char spaces[MAX_STACK_DEPTH + 1]; - memset(spaces, '.', MAX_STACK_DEPTH); - spaces[MAX_STACK_DEPTH] = '\0'; - - for (int32_t i = 0; i < MAX_THREADS; i++) - traceData.depth[i] = 2; // adjust for return from start function - - FILE* dataFp = fopen(gOptions.traceFileName, "rb"); - if (dataFp == nullptr) return; - - DataKeys* pKeys = parseKeys(dataFp, 1); - if (pKeys == nullptr) { - fclose(dataFp); - return; - } - - DataHeader dataHeader; - if (parseDataHeader(dataFp, &dataHeader) < 0) { - fclose(dataFp); - freeDataKeys(pKeys); - return; - } - - printf("Trace (threadID action usecs class.method signature):\n"); - - while (1) { - /* - * Extract values from file. - */ - int32_t threadId; - uint32_t methodVal; - uint64_t elapsedTime; - if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, &elapsedTime)) - break; - - int32_t action = METHOD_ACTION(methodVal); - int64_t methodId = METHOD_ID(methodVal); - - /* - * Generate a line of output. - */ - int64_t lastEnter = 0; - int32_t mismatch = 0; - if (action == METHOD_TRACE_ENTER) { - traceData.depth[threadId]++; - lastEnter = methodId; - } else { - /* quick test for mismatched adjacent enter/exit */ - if (lastEnter != 0 && lastEnter != methodId) mismatch = 1; - } - - int32_t printDepth = traceData.depth[threadId]; - char depthNote = ' '; - if (printDepth < 0) { - printDepth = 0; - depthNote = '-'; - } else if (printDepth > MAX_STACK_DEPTH) { - printDepth = MAX_STACK_DEPTH; - depthNote = '+'; - } - - MethodEntry* method = lookupMethod(pKeys, methodId); - if (method == nullptr) { - method = &bogusMethod; - sprintf(bogusBuf, "methodId: %#" PRIx64 "", methodId); - method->signature = bogusBuf; - } - - if (method->methodName) { - printf("%2d %s%c %8" PRIu64 "%c%s%s.%s %s\n", threadId, actionStr[action], - mismatch ? '!' : ' ', elapsedTime, depthNote, - spaces + (MAX_STACK_DEPTH - printDepth), method->className, - method->methodName, method->signature); - } else { - printf("%2d %s%c %8" PRIu64 "%c%s%s\n", threadId, actionStr[action], - mismatch ? '!' : ' ', elapsedTime, depthNote, - spaces + (MAX_STACK_DEPTH - printDepth), method->className); - } - - if (action != METHOD_TRACE_ENTER) { - traceData.depth[threadId]--; /* METHOD_TRACE_EXIT or METHOD_TRACE_UNROLL */ - lastEnter = 0; - } - - mismatch = 0; - } - - fclose(dataFp); - freeDataKeys(pKeys); -} - -/* This routine adds the given time to the parent and child methods. - * This is called when the child routine exits, after the child has - * been popped from the stack. The elapsedTime parameter is the - * duration of the child routine, including time spent in called routines. - */ -void addInclusiveTime(MethodEntry* parent, MethodEntry* child, uint64_t elapsedTime) { -#if 0 - bool verbose = false; - if (strcmp(child->className, debugClassName) == 0) - verbose = true; -#endif - - int32_t childIsRecursive = (child->recursiveEntries > 0); - int32_t parentIsRecursive = (parent->recursiveEntries > 1); - - if (child->recursiveEntries == 0) { - child->elapsedInclusive += elapsedTime; - } else if (child->recursiveEntries == 1) { - child->recursiveInclusive += elapsedTime; - } - child->numCalls[childIsRecursive] += 1; - -#if 0 - if (verbose) { - fprintf(stderr, - "%s %d elapsedTime: %lld eI: %lld, rI: %lld\n", - child->className, child->recursiveEntries, - elapsedTime, child->elapsedInclusive, - child->recursiveInclusive); - } -#endif - - /* Find the child method in the parent */ - TimedMethod* pTimed; - TimedMethod* children = parent->children[parentIsRecursive]; - for (pTimed = children; pTimed; pTimed = pTimed->next) { - if (pTimed->method == child) { - pTimed->elapsedInclusive += elapsedTime; - pTimed->numCalls += 1; - break; - } - } - if (pTimed == nullptr) { - /* Allocate a new TimedMethod */ - pTimed = new TimedMethod(); - pTimed->elapsedInclusive = elapsedTime; - pTimed->numCalls = 1; - pTimed->method = child; - - /* Add it to the front of the list */ - pTimed->next = children; - parent->children[parentIsRecursive] = pTimed; - } - - /* Find the parent method in the child */ - TimedMethod* parents = child->parents[childIsRecursive]; - for (pTimed = parents; pTimed; pTimed = pTimed->next) { - if (pTimed->method == parent) { - pTimed->elapsedInclusive += elapsedTime; - pTimed->numCalls += 1; - break; - } - } - if (pTimed == nullptr) { - /* Allocate a new TimedMethod */ - pTimed = new TimedMethod(); - pTimed->elapsedInclusive = elapsedTime; - pTimed->numCalls = 1; - pTimed->method = parent; - - /* Add it to the front of the list */ - pTimed->next = parents; - child->parents[childIsRecursive] = pTimed; - } - -#if 0 - if (verbose) { - fprintf(stderr, - " %s %d eI: %lld\n", - parent->className, parent->recursiveEntries, - pTimed->elapsedInclusive); - } -#endif -} - -/* Sorts a linked list and returns a newly allocated array containing - * the sorted entries. - */ -TimedMethod* sortTimedMethodList(TimedMethod* list, int32_t* num) { - /* Count the elements */ - TimedMethod* pTimed; - int32_t num_entries = 0; - for (pTimed = list; pTimed; pTimed = pTimed->next) num_entries += 1; - *num = num_entries; - if (num_entries == 0) return nullptr; - - /* Copy all the list elements to a new array and sort them */ - int32_t ii; - TimedMethod* sorted = new TimedMethod[num_entries]; - for (ii = 0, pTimed = list; pTimed; pTimed = pTimed->next, ++ii) - memcpy(&sorted[ii], pTimed, sizeof(TimedMethod)); - qsort(sorted, num_entries, sizeof(TimedMethod), compareTimedMethod); - - /* Fix up the "next" pointers so that they work. */ - for (ii = 0; ii < num_entries - 1; ++ii) sorted[ii].next = &sorted[ii + 1]; - sorted[num_entries - 1].next = nullptr; - - return sorted; -} - -/* Define flag values for printInclusiveMethod() */ -static const int32_t kIsRecursive = 1; - -/* This prints the inclusive stats for all the parents or children of a - * method, depending on the list that is passed in. - */ -void printInclusiveMethod(MethodEntry* method, TimedMethod* list, int32_t numCalls, int32_t flags) { - char buf[80]; - const char* anchor_close = ""; - const char* spaces = " "; /* 6 spaces */ - int32_t num_spaces = strlen(spaces); - const char* space_ptr = &spaces[num_spaces]; - char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; - char signatureBuf[HTML_BUFSIZE]; - - if (gOptions.outputHtml) anchor_close = ""; - - int32_t num; - TimedMethod* sorted = sortTimedMethodList(list, &num); - double methodTotal = method->elapsedInclusive; - for (TimedMethod* pTimed = sorted; pTimed; pTimed = pTimed->next) { - MethodEntry* relative = pTimed->method; - const char* className = relative->className; - const char* methodName = relative->methodName; - const char* signature = relative->signature; - double per = 100.0 * pTimed->elapsedInclusive / methodTotal; - sprintf(buf, "[%d]", relative->index); - if (gOptions.outputHtml) { - int32_t len = strlen(buf); - if (len > num_spaces) len = num_spaces; - sprintf(buf, "[%d]", relative->index, relative->index); - space_ptr = &spaces[len]; - className = htmlEscape(className, classBuf, HTML_BUFSIZE); - methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); - signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); - } - int32_t nCalls = numCalls; - if (nCalls == 0) nCalls = relative->numCalls[0] + relative->numCalls[1]; - if (relative->methodName) { - if (flags & kIsRecursive) { - // Don't display percentages for recursive functions - printf("%6s %5s %6s %s%6s%s %6d/%-6d %9" PRIu64 " %s.%s %s\n", "", "", - "", space_ptr, buf, anchor_close, pTimed->numCalls, nCalls, - pTimed->elapsedInclusive, className, methodName, signature); - } else { - printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9" PRIu64 " %s.%s %s\n", "", - "", per, space_ptr, buf, anchor_close, pTimed->numCalls, nCalls, - pTimed->elapsedInclusive, className, methodName, signature); - } - } else { - if (flags & kIsRecursive) { - // Don't display percentages for recursive functions - printf("%6s %5s %6s %s%6s%s %6d/%-6d %9" PRIu64 " %s\n", "", "", "", - space_ptr, buf, anchor_close, pTimed->numCalls, nCalls, - pTimed->elapsedInclusive, className); - } else { - printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9" PRIu64 " %s\n", "", "", - per, space_ptr, buf, anchor_close, pTimed->numCalls, nCalls, - pTimed->elapsedInclusive, className); - } - } - } -} - -void countRecursiveEntries(CallStack* pStack, int32_t top, MethodEntry* method) { - method->recursiveEntries = 0; - for (int32_t ii = 0; ii < top; ++ii) { - if (pStack->calls[ii].method == method) method->recursiveEntries += 1; - } -} - -void stackDump(CallStack* pStack, int32_t top) { - for (int32_t ii = 0; ii < top; ++ii) { - MethodEntry* method = pStack->calls[ii].method; - uint64_t entryTime = pStack->calls[ii].entryTime; - if (method->methodName) { - fprintf(stderr, " %2d: %8" PRIu64 " %s.%s %s\n", ii, entryTime, - method->className, method->methodName, method->signature); - } else { - fprintf(stderr, " %2d: %8" PRIu64 " %s\n", ii, entryTime, method->className); - } - } -} - -void outputTableOfContents() { - printf("\n"); - printf("

Table of Contents

\n"); - printf("\n\n"); -} - -void outputNavigationBar() { - printf("[Top]\n"); - printf("[Exclusive]\n"); - printf("[Inclusive]\n"); - printf("[Class]\n"); - printf("[Method]\n"); - printf("

\n"); -} - -void printExclusiveProfile(MethodEntry** pMethods, int32_t numMethods, uint64_t sumThreadTime) { - char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; - char signatureBuf[HTML_BUFSIZE]; - const char* anchor_close = ""; - char anchor_buf[80]; - anchor_buf[0] = 0; - if (gOptions.outputHtml) { - anchor_close = ""; - printf("\n"); - printf("
\n"); - outputNavigationBar(); - } else { - printf("\n%s\n", profileSeparator); - } - - /* First, sort the methods into decreasing order of inclusive - * elapsed time so that we can assign the method indices. - */ - qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedInclusive); - - for (int32_t ii = 0; ii < numMethods; ++ii) pMethods[ii]->index = ii; - - /* Sort the methods into decreasing order of exclusive elapsed time. */ - qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedExclusive); - - printf("Total cycles: %" PRIu64 "\n\n", sumThreadTime); - if (gOptions.outputHtml) { - printf("

\n"); - } - printf("Exclusive elapsed times for each method, not including time spent in\n"); - printf("children, sorted by exclusive time.\n\n"); - if (gOptions.outputHtml) { - printf("

\n
\n");
-  }
-
-  printf("    Usecs  self %%  sum %%  Method\n");
-
-  double sum = 0;
-  double total = sumThreadTime;
-  for (int32_t ii = 0; ii < numMethods; ++ii) {
-    MethodEntry* method = pMethods[ii];
-    /* Don't show methods with zero cycles */
-    if (method->elapsedExclusive == 0) break;
-    const char* className = method->className;
-    const char* methodName = method->methodName;
-    const char* signature = method->signature;
-    sum += method->elapsedExclusive;
-    double per = 100.0 * method->elapsedExclusive / total;
-    double sum_per = 100.0 * sum / total;
-    if (gOptions.outputHtml) {
-      sprintf(anchor_buf, "", method->index);
-      className = htmlEscape(className, classBuf, HTML_BUFSIZE);
-      methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
-      signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
-    }
-    if (method->methodName) {
-      printf("%9" PRIu64 "  %6.2f %6.2f  %s[%d]%s %s.%s %s\n",
-             method->elapsedExclusive, per, sum_per, anchor_buf, method->index,
-             anchor_close, className, methodName, signature);
-    } else {
-      printf("%9" PRIu64 "  %6.2f %6.2f  %s[%d]%s %s\n",
-             method->elapsedExclusive, per, sum_per, anchor_buf, method->index,
-             anchor_close, className);
-    }
-  }
-  if (gOptions.outputHtml) {
-    printf("
\n"); - } -} - -/* check to make sure that the child method meets the threshold of the parent */ -int32_t checkThreshold(MethodEntry* parent, MethodEntry* child) { - double parentTime = parent->elapsedInclusive; - double childTime = child->elapsedInclusive; - int64_t percentage = (childTime / parentTime) * 100.0; - return (percentage < gOptions.threshold) ? 0 : 1; -} - -void createLabels(FILE* file, MethodEntry* method) { - fprintf(file, - "node%d[label = \"[%d] %s.%s (%" PRIu64 ", %" PRIu64 ", %d)\"]\n", - method->index, method->index, method->className, method->methodName, - method->elapsedInclusive / 1000, method->elapsedExclusive / 1000, - method->numCalls[0]); - - method->graphState = GRAPH_LABEL_VISITED; - - for (TimedMethod* child = method->children[0]; child; child = child->next) { - MethodEntry* childMethod = child->method; - - if ((childMethod->graphState & GRAPH_LABEL_VISITED) == 0 && - checkThreshold(method, childMethod)) { - createLabels(file, child->method); - } - } -} - -void createLinks(FILE* file, MethodEntry* method) { - method->graphState |= GRAPH_NODE_VISITED; - - for (TimedMethod* child = method->children[0]; child; child = child->next) { - MethodEntry* childMethod = child->method; - if (checkThreshold(method, child->method)) { - fprintf(file, "node%d -> node%d\n", method->index, child->method->index); - // only visit children that haven't been visited before - if ((childMethod->graphState & GRAPH_NODE_VISITED) == 0) { - createLinks(file, child->method); - } - } - } -} - -void createInclusiveProfileGraphNew(DataKeys* dataKeys) { - // create a temporary file in /tmp - char path[FILENAME_MAX]; - if (gOptions.keepDotFile) { - snprintf(path, FILENAME_MAX, "%s.dot", gOptions.graphFileName); - } else { - snprintf(path, FILENAME_MAX, "dot-%d-%d.dot", (int32_t)time(nullptr), rand()); - } - - FILE* file = fopen(path, "w+"); - - fprintf(file, "digraph g {\nnode [shape = record,height=.1];\n"); - - createLabels(file, dataKeys->methods); - createLinks(file, dataKeys->methods); - - fprintf(file, "}"); - fclose(file); - - // now that we have the dot file generate the image - char command[1024]; - snprintf(command, 1024, "dot -Tpng -o \"%s\" \"%s\"", gOptions.graphFileName, path); - - system(command); - - if (!gOptions.keepDotFile) { - remove(path); - } -} - -void printInclusiveProfile(MethodEntry** pMethods, int32_t numMethods, uint64_t sumThreadTime) { - char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; - char signatureBuf[HTML_BUFSIZE]; - char anchor_buf[80]; - const char* anchor_close = ""; - anchor_buf[0] = 0; - if (gOptions.outputHtml) { - anchor_close = ""; - printf("\n"); - printf("
\n"); - outputNavigationBar(); - } else { - printf("\n%s\n", profileSeparator); - } - - /* Sort the methods into decreasing order of inclusive elapsed time. */ - qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedInclusive); - - printf("\nInclusive elapsed times for each method and its parents and children,\n"); - printf("sorted by inclusive time.\n\n"); - - if (gOptions.outputHtml) { - printf("

\n
\n");
-  }
-
-  printf("index  %%/total %%/self  index     calls         usecs name\n");
-
-  double total = sumThreadTime;
-  for (int32_t ii = 0; ii < numMethods; ++ii) {
-    char buf[40];
-
-    MethodEntry* method = pMethods[ii];
-    /* Don't show methods with zero cycles */
-    if (method->elapsedInclusive == 0) break;
-
-    const char* className = method->className;
-    const char* methodName = method->methodName;
-    const char* signature = method->signature;
-
-    if (gOptions.outputHtml) {
-      printf("", method->index);
-      className = htmlEscape(className, classBuf, HTML_BUFSIZE);
-      methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
-      signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
-    }
-    printf("----------------------------------------------------\n");
-
-    /* Sort and print the parents */
-    int32_t numCalls = method->numCalls[0] + method->numCalls[1];
-    printInclusiveMethod(method, method->parents[0], numCalls, 0);
-    if (method->parents[1]) {
-      printf("               +++++++++++++++++++++++++\n");
-      printInclusiveMethod(method, method->parents[1], numCalls, kIsRecursive);
-    }
-
-    double per = 100.0 * method->elapsedInclusive / total;
-    sprintf(buf, "[%d]", ii);
-    if (method->methodName) {
-      printf("%-6s %5.1f%%   %5s %6s %6d+%-6d %9" PRIu64 " %s.%s %s\n", buf,
-             per, "", "", method->numCalls[0], method->numCalls[1],
-             method->elapsedInclusive, className, methodName, signature);
-    } else {
-      printf("%-6s %5.1f%%   %5s %6s %6d+%-6d %9" PRIu64 " %s\n", buf, per, "",
-             "", method->numCalls[0], method->numCalls[1],
-             method->elapsedInclusive, className);
-    }
-    double excl_per = 100.0 * method->topExclusive / method->elapsedInclusive;
-    printf("%6s %5s   %5.1f%% %6s %6s %6s %9" PRIu64 "\n", "", "", excl_per,
-           "excl", "", "", method->topExclusive);
-
-    /* Sort and print the children */
-    printInclusiveMethod(method, method->children[0], 0, 0);
-    if (method->children[1]) {
-      printf("               +++++++++++++++++++++++++\n");
-      printInclusiveMethod(method, method->children[1], 0, kIsRecursive);
-    }
-  }
-  if (gOptions.outputHtml) {
-    printf("
\n"); - } -} - -void createClassList(TraceData* traceData, MethodEntry** pMethods, int32_t numMethods) { - /* Sort the methods into alphabetical order to find the unique class - * names. - */ - qsort(pMethods, numMethods, sizeof(MethodEntry*), compareClassNames); - - /* Count the number of unique class names. */ - const char* currentClassName = ""; - const char* firstClassName = nullptr; - traceData->numClasses = 0; - for (int32_t ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == nullptr) { - continue; - } - if (strcmp(pMethods[ii]->className, currentClassName) != 0) { - // Remember the first one - if (firstClassName == nullptr) { - firstClassName = pMethods[ii]->className; - } - traceData->numClasses += 1; - currentClassName = pMethods[ii]->className; - } - } - - if (traceData->numClasses == 0) { - traceData->classes = nullptr; - return; - } - - /* Allocate space for all of the unique class names */ - traceData->classes = new ClassEntry[traceData->numClasses]; - - /* Initialize the classes array */ - memset(traceData->classes, 0, sizeof(ClassEntry) * traceData->numClasses); - ClassEntry* pClass = traceData->classes; - pClass->className = currentClassName = firstClassName; - int32_t prevNumMethods = 0; - for (int32_t ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == nullptr) { - continue; - } - if (strcmp(pMethods[ii]->className, currentClassName) != 0) { - pClass->numMethods = prevNumMethods; - (++pClass)->className = currentClassName = pMethods[ii]->className; - prevNumMethods = 0; - } - prevNumMethods += 1; - } - pClass->numMethods = prevNumMethods; - - /* Create the array of MethodEntry pointers for each class */ - pClass = nullptr; - currentClassName = ""; - int32_t nextMethod = 0; - for (int32_t ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == nullptr) { - continue; - } - if (strcmp(pMethods[ii]->className, currentClassName) != 0) { - currentClassName = pMethods[ii]->className; - if (pClass == nullptr) - pClass = traceData->classes; - else - pClass++; - /* Allocate space for the methods array */ - pClass->methods = new MethodEntry*[pClass->numMethods]; - nextMethod = 0; - } - pClass->methods[nextMethod++] = pMethods[ii]; - } -} - -/* Prints a number of html non-breaking spaces according so that the length - * of the string "buf" is at least "width" characters wide. If width is - * negative, then trailing spaces are added instead of leading spaces. - */ -void printHtmlField(char* buf, int32_t width) { - int32_t leadingSpaces = 1; - if (width < 0) { - width = -width; - leadingSpaces = 0; - } - int32_t len = strlen(buf); - int32_t numSpaces = width - len; - if (numSpaces <= 0) { - printf("%s", buf); - return; - } - if (leadingSpaces == 0) printf("%s", buf); - for (int32_t ii = 0; ii < numSpaces; ++ii) printf(" "); - if (leadingSpaces == 1) printf("%s", buf); -} - -void printClassProfiles(TraceData* traceData, uint64_t sumThreadTime) { - char classBuf[HTML_BUFSIZE]; - char methodBuf[HTML_BUFSIZE]; - char signatureBuf[HTML_BUFSIZE]; - - if (gOptions.outputHtml) { - printf("\n"); - printf("
\n"); - outputNavigationBar(); - } else { - printf("\n%s\n", profileSeparator); - } - - if (traceData->numClasses == 0) { - printf("\nNo classes.\n"); - if (gOptions.outputHtml) { - printf("

\n"); - } - return; - } - - printf("\nExclusive elapsed time for each class, summed over all the methods\n"); - printf("in the class.\n\n"); - if (gOptions.outputHtml) { - printf("

\n"); - } - - /* For each class, sum the exclusive times in all of the methods - * in that class. Also sum the number of method calls. Also - * sort the methods so the most expensive appear at the top. - */ - ClassEntry* pClass = traceData->classes; - for (int32_t ii = 0; ii < traceData->numClasses; ++ii, ++pClass) { - // printf("%s %d methods\n", pClass->className, pClass->numMethods); - int32_t numMethods = pClass->numMethods; - for (int32_t jj = 0; jj < numMethods; ++jj) { - MethodEntry* method = pClass->methods[jj]; - pClass->elapsedExclusive += method->elapsedExclusive; - pClass->numCalls[0] += method->numCalls[0]; - pClass->numCalls[1] += method->numCalls[1]; - } - - /* Sort the methods into decreasing order of exclusive time */ - qsort(pClass->methods, numMethods, sizeof(MethodEntry*), compareElapsedExclusive); - } - - /* Allocate an array of pointers to the classes for more efficient sorting. */ - ClassEntry** pClasses = new ClassEntry*[traceData->numClasses]; - for (int32_t ii = 0; ii < traceData->numClasses; ++ii) - pClasses[ii] = &traceData->classes[ii]; - - /* Sort the classes into decreasing order of exclusive time */ - qsort(pClasses, traceData->numClasses, sizeof(ClassEntry*), compareClassExclusive); - - if (gOptions.outputHtml) { - printf( - "
    "); - printf("Cycles %%/total Cumul.%%  Calls+Recur  Class
\n"); - } else { - printf(" Cycles %%/total Cumul.%% Calls+Recur Class\n"); - } - - double sum = 0; - double total = sumThreadTime; - for (int32_t ii = 0; ii < traceData->numClasses; ++ii) { - /* Skip classes with zero cycles */ - pClass = pClasses[ii]; - if (pClass->elapsedExclusive == 0) break; - - sum += pClass->elapsedExclusive; - double per = 100.0 * pClass->elapsedExclusive / total; - double sum_per = 100.0 * sum / total; - const char* className = pClass->className; - if (gOptions.outputHtml) { - char buf[80]; - - className = htmlEscape(className, classBuf, HTML_BUFSIZE); - printf( - "
+", - ii, ii); - sprintf(buf, "%" PRIu64, pClass->elapsedExclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%.1f", per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%.1f", sum_per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%d", pClass->numCalls[0]); - printHtmlField(buf, 6); - printf("+"); - sprintf(buf, "%d", pClass->numCalls[1]); - printHtmlField(buf, -6); - printf(" "); - printf("%s", className); - printf("
\n"); - printf("
\n", ii); - } else { - printf("---------------------------------------------\n"); - printf("%9" PRIu64 " %7.1f %7.1f %6d+%-6d %s\n", pClass->elapsedExclusive, - per, sum_per, pClass->numCalls[0], pClass->numCalls[1], className); - } - - int32_t numMethods = pClass->numMethods; - double classExclusive = pClass->elapsedExclusive; - double sumMethods = 0; - for (int32_t jj = 0; jj < numMethods; ++jj) { - MethodEntry* method = pClass->methods[jj]; - const char* methodName = method->methodName; - const char* signature = method->signature; - per = 100.0 * method->elapsedExclusive / classExclusive; - sumMethods += method->elapsedExclusive; - sum_per = 100.0 * sumMethods / classExclusive; - if (gOptions.outputHtml) { - char buf[80]; - - methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); - signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); - printf("
 "); - sprintf(buf, "%" PRIu64, method->elapsedExclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%" PRIu64, method->elapsedInclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%.1f", per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%.1f", sum_per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%d", method->numCalls[0]); - printHtmlField(buf, 6); - printf("+"); - sprintf(buf, "%d", method->numCalls[1]); - printHtmlField(buf, -6); - printf(" "); - printf("[%d] %s %s", method->index, - method->index, methodName, signature); - printf("
\n"); - } else { - printf("%9" PRIu64 " %9" PRIu64 " %7.1f %7.1f %6d+%-6d [%d] %s %s\n", - method->elapsedExclusive, method->elapsedInclusive, per, sum_per, - method->numCalls[0], method->numCalls[1], method->index, - methodName, signature); - } - } - if (gOptions.outputHtml) { - printf("
\n"); - } - } -} - -void createUniqueMethodList(TraceData* traceData, MethodEntry** pMethods, int32_t numMethods) { - /* Sort the methods into alphabetical order of method names - * to find the unique method names. - */ - qsort(pMethods, numMethods, sizeof(MethodEntry*), compareMethodNames); - - /* Count the number of unique method names, ignoring class and signature. */ - const char* currentMethodName = ""; - traceData->numUniqueMethods = 0; - for (int32_t ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == nullptr) continue; - if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) { - traceData->numUniqueMethods += 1; - currentMethodName = pMethods[ii]->methodName; - } - } - if (traceData->numUniqueMethods == 0) return; - - /* Allocate space for pointers to all of the unique methods */ - traceData->uniqueMethods = new UniqueMethodEntry[traceData->numUniqueMethods]; - - /* Initialize the uniqueMethods array */ - memset(traceData->uniqueMethods, 0, sizeof(UniqueMethodEntry) * traceData->numUniqueMethods); - UniqueMethodEntry* pUnique = traceData->uniqueMethods; - currentMethodName = nullptr; - int32_t prevNumMethods = 0; - for (int32_t ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == nullptr) continue; - if (currentMethodName == nullptr) currentMethodName = pMethods[ii]->methodName; - if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) { - currentMethodName = pMethods[ii]->methodName; - pUnique->numMethods = prevNumMethods; - pUnique++; - prevNumMethods = 0; - } - prevNumMethods += 1; - } - pUnique->numMethods = prevNumMethods; - - /* Create the array of MethodEntry pointers for each unique method */ - pUnique = nullptr; - currentMethodName = ""; - int32_t nextMethod = 0; - for (int32_t ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == nullptr) continue; - if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) { - currentMethodName = pMethods[ii]->methodName; - if (pUnique == nullptr) - pUnique = traceData->uniqueMethods; - else - pUnique++; - /* Allocate space for the methods array */ - pUnique->methods = new MethodEntry*[pUnique->numMethods]; - nextMethod = 0; - } - pUnique->methods[nextMethod++] = pMethods[ii]; - } -} - -void printMethodProfiles(TraceData* traceData, uint64_t sumThreadTime) { - char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; - char signatureBuf[HTML_BUFSIZE]; - - if (traceData->numUniqueMethods == 0) return; - - if (gOptions.outputHtml) { - printf("\n"); - printf("
\n"); - outputNavigationBar(); - } else { - printf("\n%s\n", profileSeparator); - } - - printf("\nExclusive elapsed time for each method, summed over all the classes\n"); - printf("that contain a method with the same name.\n\n"); - if (gOptions.outputHtml) { - printf("

\n"); - } - - /* For each unique method, sum the exclusive times in all of the methods - * with the same name. Also sum the number of method calls. Also - * sort the methods so the most expensive appear at the top. - */ - UniqueMethodEntry* pUnique = traceData->uniqueMethods; - for (int32_t ii = 0; ii < traceData->numUniqueMethods; ++ii, ++pUnique) { - int32_t numMethods = pUnique->numMethods; - for (int32_t jj = 0; jj < numMethods; ++jj) { - MethodEntry* method = pUnique->methods[jj]; - pUnique->elapsedExclusive += method->elapsedExclusive; - pUnique->numCalls[0] += method->numCalls[0]; - pUnique->numCalls[1] += method->numCalls[1]; - } - - /* Sort the methods into decreasing order of exclusive time */ - qsort(pUnique->methods, numMethods, sizeof(MethodEntry*), compareElapsedExclusive); - } - - /* Allocate an array of pointers to the methods for more efficient sorting. */ - UniqueMethodEntry** pUniqueMethods = new UniqueMethodEntry*[traceData->numUniqueMethods]; - for (int32_t ii = 0; ii < traceData->numUniqueMethods; ++ii) - pUniqueMethods[ii] = &traceData->uniqueMethods[ii]; - - /* Sort the methods into decreasing order of exclusive time */ - qsort(pUniqueMethods, traceData->numUniqueMethods, sizeof(UniqueMethodEntry*), - compareUniqueExclusive); - - if (gOptions.outputHtml) { - printf( - "
    "); - printf("Cycles %%/total Cumul.%%  Calls+Recur  Method
\n"); - } else { - printf(" Cycles %%/total Cumul.%% Calls+Recur Method\n"); - } - - double sum = 0; - double total = sumThreadTime; - for (int32_t ii = 0; ii < traceData->numUniqueMethods; ++ii) { - /* Skip methods with zero cycles */ - pUnique = pUniqueMethods[ii]; - if (pUnique->elapsedExclusive == 0) break; - - sum += pUnique->elapsedExclusive; - double per = 100.0 * pUnique->elapsedExclusive / total; - double sum_per = 100.0 * sum / total; - const char* methodName = pUnique->methods[0]->methodName; - if (gOptions.outputHtml) { - char buf[80]; - - methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); - printf( - "
+", - ii, ii); - sprintf(buf, "%" PRIu64, pUnique->elapsedExclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%.1f", per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%.1f", sum_per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%d", pUnique->numCalls[0]); - printHtmlField(buf, 6); - printf("+"); - sprintf(buf, "%d", pUnique->numCalls[1]); - printHtmlField(buf, -6); - printf(" "); - printf("%s", methodName); - printf("
\n"); - printf("
\n", ii); - } else { - printf("---------------------------------------------\n"); - printf("%9" PRIu64 " %7.1f %7.1f %6d+%-6d %s\n", - pUnique->elapsedExclusive, per, sum_per, pUnique->numCalls[0], - pUnique->numCalls[1], methodName); - } - int32_t numMethods = pUnique->numMethods; - double methodExclusive = pUnique->elapsedExclusive; - double sumMethods = 0; - for (int32_t jj = 0; jj < numMethods; ++jj) { - MethodEntry* method = pUnique->methods[jj]; - const char* className = method->className; - const char* signature = method->signature; - per = 100.0 * method->elapsedExclusive / methodExclusive; - sumMethods += method->elapsedExclusive; - sum_per = 100.0 * sumMethods / methodExclusive; - if (gOptions.outputHtml) { - char buf[80]; - - className = htmlEscape(className, classBuf, HTML_BUFSIZE); - signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); - printf("
 "); - sprintf(buf, "%" PRIu64, method->elapsedExclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%" PRIu64, method->elapsedInclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%.1f", per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%.1f", sum_per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%d", method->numCalls[0]); - printHtmlField(buf, 6); - printf("+"); - sprintf(buf, "%d", method->numCalls[1]); - printHtmlField(buf, -6); - printf(" "); - printf("[%d] %s.%s %s", method->index, - method->index, className, methodName, signature); - printf("
\n"); - } else { - printf("%9" PRIu64 " %9" PRIu64 " %7.1f %7.1f %6d+%-6d [%d] %s.%s %s\n", - method->elapsedExclusive, method->elapsedInclusive, per, sum_per, - method->numCalls[0], method->numCalls[1], method->index, - className, methodName, signature); - } - } - if (gOptions.outputHtml) { - printf("
\n"); - } - } -} - -/* - * Read the key and data files and return the MethodEntries for those files - */ -DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName, uint64_t* threadTime) { - MethodEntry* caller; - - FILE* dataFp = fopen(traceFileName, "rb"); - if (dataFp == nullptr) return nullptr; - - DataKeys* dataKeys = parseKeys(dataFp, 0); - if (dataKeys == nullptr) { - fclose(dataFp); - return nullptr; - } - - DataHeader dataHeader; - if (parseDataHeader(dataFp, &dataHeader) < 0) { - fclose(dataFp); - return dataKeys; - } - -#if 0 - FILE* dumpStream = fopen("debug", "w"); -#endif - while (1) { - /* - * Extract values from file. - */ - int32_t threadId; - uint32_t methodVal; - uint64_t currentTime; - if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, ¤tTime)) - break; - - int32_t action = METHOD_ACTION(methodVal); - int64_t methodId = METHOD_ID(methodVal); - - /* Get the call stack for this thread */ - CallStack* pStack = traceData->stacks[threadId]; - - /* If there is no call stack yet for this thread, then allocate one */ - if (pStack == nullptr) { - pStack = new CallStack(); - pStack->top = 0; - pStack->lastEventTime = currentTime; - pStack->threadStartTime = currentTime; - traceData->stacks[threadId] = pStack; - } - - /* Lookup the current method */ - MethodEntry* method = lookupMethod(dataKeys, methodId); - if (method == nullptr) method = &dataKeys->methods[UNKNOWN_INDEX]; - -#if 0 - if (method->methodName) { - fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s.%s %s\n", - threadId, currentTime, action, pStack->threadStartTime, - method->recursiveEntries, - pStack->top, method->className, method->methodName, - method->signature); - } else { - fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s\n", - threadId, currentTime, action, pStack->threadStartTime, - method->recursiveEntries, - pStack->top, method->className); - } -#endif - - if (action == METHOD_TRACE_ENTER) { - /* This is a method entry */ - if (pStack->top >= MAX_STACK_DEPTH) { - fprintf(stderr, "Stack overflow (exceeded %d frames)\n", - MAX_STACK_DEPTH); - exit(1); - } - - /* Get the caller method */ - if (pStack->top >= 1) - caller = pStack->calls[pStack->top - 1].method; - else - caller = &dataKeys->methods[TOPLEVEL_INDEX]; - countRecursiveEntries(pStack, pStack->top, caller); - caller->elapsedExclusive += currentTime - pStack->lastEventTime; -#if 0 - if (caller->elapsedExclusive > 10000000) - fprintf(dumpStream, "%llu current %llu last %llu diff %llu\n", - caller->elapsedExclusive, currentTime, - pStack->lastEventTime, - currentTime - pStack->lastEventTime); -#endif - if (caller->recursiveEntries <= 1) { - caller->topExclusive += currentTime - pStack->lastEventTime; - } - - /* Push the method on the stack for this thread */ - pStack->calls[pStack->top].method = method; - pStack->calls[pStack->top++].entryTime = currentTime; - } else { - /* This is a method exit */ - uint64_t entryTime = 0; - - /* Pop the method off the stack for this thread */ - if (pStack->top > 0) { - pStack->top -= 1; - entryTime = pStack->calls[pStack->top].entryTime; - if (method != pStack->calls[pStack->top].method) { - if (method->methodName) { - fprintf(stderr, "Exit from method %s.%s %s does not match stack:\n", - method->className, method->methodName, method->signature); - } else { - fprintf(stderr, "Exit from method %s does not match stack:\n", - method->className); - } - stackDump(pStack, pStack->top + 1); - exit(1); - } - } - - /* Get the caller method */ - if (pStack->top >= 1) - caller = pStack->calls[pStack->top - 1].method; - else - caller = &dataKeys->methods[TOPLEVEL_INDEX]; - countRecursiveEntries(pStack, pStack->top, caller); - countRecursiveEntries(pStack, pStack->top, method); - uint64_t elapsed = currentTime - entryTime; - addInclusiveTime(caller, method, elapsed); - method->elapsedExclusive += currentTime - pStack->lastEventTime; - if (method->recursiveEntries == 0) { - method->topExclusive += currentTime - pStack->lastEventTime; - } - } - /* Remember the time of the last entry or exit event */ - pStack->lastEventTime = currentTime; - } - - /* If we have calls on the stack when the trace ends, then clean - * up the stack and add time to the callers by pretending that we - * are exiting from their methods now. - */ - uint64_t sumThreadTime = 0; - for (int32_t threadId = 0; threadId < MAX_THREADS; ++threadId) { - CallStack* pStack = traceData->stacks[threadId]; - - /* If this thread never existed, then continue with next thread */ - if (pStack == nullptr) continue; - - /* Also, add up the time taken by all of the threads */ - sumThreadTime += pStack->lastEventTime - pStack->threadStartTime; - - for (int32_t ii = 0; ii < pStack->top; ++ii) { - if (ii == 0) - caller = &dataKeys->methods[TOPLEVEL_INDEX]; - else - caller = pStack->calls[ii - 1].method; - MethodEntry* method = pStack->calls[ii].method; - countRecursiveEntries(pStack, ii, caller); - countRecursiveEntries(pStack, ii, method); - - uint64_t entryTime = pStack->calls[ii].entryTime; - uint64_t elapsed = pStack->lastEventTime - entryTime; - addInclusiveTime(caller, method, elapsed); - } - } - caller = &dataKeys->methods[TOPLEVEL_INDEX]; - caller->elapsedInclusive = sumThreadTime; - -#if 0 - fclose(dumpStream); -#endif - - if (threadTime != nullptr) { - *threadTime = sumThreadTime; - } - - fclose(dataFp); - return dataKeys; -} - -MethodEntry** parseMethodEntries(DataKeys* dataKeys) { - /* Create a new array of pointers to the methods and sort the pointers - * instead of the actual MethodEntry structs. We need to do this - * because there are other lists that contain pointers to the - * MethodEntry structs. - */ - MethodEntry** pMethods = new MethodEntry*[dataKeys->numMethods]; - for (int32_t ii = 0; ii < dataKeys->numMethods; ++ii) { - MethodEntry* entry = &dataKeys->methods[ii]; - pMethods[ii] = entry; - } - - return pMethods; -} - -/* - * Produce a function profile from the following methods - */ -void profileTrace(TraceData* traceData, MethodEntry** pMethods, int32_t numMethods, - uint64_t sumThreadTime) { - /* Print the html header, if necessary */ - if (gOptions.outputHtml) { - printf(htmlHeader, gOptions.sortableUrl); - outputTableOfContents(); - } - - printExclusiveProfile(pMethods, numMethods, sumThreadTime); - printInclusiveProfile(pMethods, numMethods, sumThreadTime); - - createClassList(traceData, pMethods, numMethods); - printClassProfiles(traceData, sumThreadTime); - - createUniqueMethodList(traceData, pMethods, numMethods); - printMethodProfiles(traceData, sumThreadTime); - - if (gOptions.outputHtml) { - printf("%s", htmlFooter); - } -} - -int32_t compareMethodNamesForDiff(const void* a, const void* b) { - const MethodEntry* methodA = *(const MethodEntry**) a; - const MethodEntry* methodB = *(const MethodEntry**) b; - if (methodA->methodName == nullptr || methodB->methodName == nullptr) { - return compareClassNames(a, b); - } - int32_t result = strcmp(methodA->methodName, methodB->methodName); - if (result == 0) { - result = strcmp(methodA->signature, methodB->signature); - if (result == 0) { - return strcmp(methodA->className, methodB->className); - } - } - return result; -} - -int32_t findMatch(MethodEntry** methods, int32_t size, MethodEntry* matchThis) { - for (int32_t i = 0; i < size; i++) { - MethodEntry* method = methods[i]; - - if (method != nullptr && !compareMethodNamesForDiff(&method, &matchThis)) { - // printf("%s.%s == %s.%s
\n", matchThis->className, matchThis->methodName, - // method->className, method->methodName); - - return i; - // if (!compareMethodNames(&method, &matchThis)) return i; - } - } - - return -1; -} - -int32_t compareDiffEntriesExculsive(const void* a, const void* b) { - const DiffEntry* entryA = (const DiffEntry*) a; - const DiffEntry* entryB = (const DiffEntry*) b; - - if (entryA->differenceExclusive < entryB->differenceExclusive) { - return 1; - } else if (entryA->differenceExclusive > entryB->differenceExclusive) { - return -1; - } - - return 0; -} - -int32_t compareDiffEntriesInculsive(const void* a, const void* b) { - const DiffEntry* entryA = (const DiffEntry*) a; - const DiffEntry* entryB = (const DiffEntry*) b; - - if (entryA->differenceInclusive < entryB->differenceInclusive) { - return 1; - } else if (entryA->differenceInclusive > entryB->differenceInclusive) { - return -1; - } - - return 0; -} - -void printMissingMethod(MethodEntry* method) { - char classBuf[HTML_BUFSIZE]; - char methodBuf[HTML_BUFSIZE]; - - char* className = htmlEscape(method->className, classBuf, HTML_BUFSIZE); - char* methodName = htmlEscape(method->methodName, methodBuf, HTML_BUFSIZE); - - if (gOptions.outputHtml) printf("
\n"); - - ptr++; - } - - if (gOptions.outputHtml) printf("
MethodExclusiveInclusive# calls
\n"); - - printf("%s.%s ", className, methodName); - if (gOptions.outputHtml) printf(""); - - printf("%" PRIu64 " ", method->elapsedExclusive); - if (gOptions.outputHtml) printf(""); - - printf("%" PRIu64 " ", method->elapsedInclusive); - if (gOptions.outputHtml) printf(""); - - printf("%d\n", method->numCalls[0]); - if (gOptions.outputHtml) printf("\n"); -} - -void createDiff(DataKeys* d1, DataKeys* d2) { - MethodEntry** methods1 = parseMethodEntries(d1); - MethodEntry** methods2 = parseMethodEntries(d2); - - // sort and assign the indicies - qsort(methods1, d1->numMethods, sizeof(MethodEntry*), compareElapsedInclusive); - for (int32_t i = 0; i < d1->numMethods; ++i) { - methods1[i]->index = i; - } - - qsort(methods2, d2->numMethods, sizeof(MethodEntry*), compareElapsedInclusive); - for (int32_t i = 0; i < d2->numMethods; ++i) { - methods2[i]->index = i; - } - - int32_t max = (d1->numMethods < d2->numMethods) ? d2->numMethods : d1->numMethods; - max++; - DiffEntry* diffs = new DiffEntry[max]; - memset(diffs, 0, max * sizeof(DiffEntry)); - DiffEntry* ptr = diffs; - - // printf("
d1->numMethods: %d d1->numMethods: %d
\n", - // d1->numMethods, d2->numMethods); - - int32_t matches = 0; - - for (int32_t i = 0; i < d1->numMethods; i++) { - int32_t match = findMatch(methods2, d2->numMethods, methods1[i]); - if (match >= 0) { - ptr->method1 = methods1[i]; - ptr->method2 = methods2[match]; - - uint64_t e1 = ptr->method1->elapsedExclusive; - uint64_t e2 = ptr->method2->elapsedExclusive; - if (e1 > 0) { - ptr->differenceExclusive = e2 - e1; - ptr->differenceExclusivePercentage = (static_cast(e2) / - static_cast(e1)) * 100.0; - } - - uint64_t i1 = ptr->method1->elapsedInclusive; - uint64_t i2 = ptr->method2->elapsedInclusive; - if (i1 > 0) { - ptr->differenceInclusive = i2 - i1; - ptr->differenceInclusivePercentage = (static_cast(i2) / - static_cast(i1)) * 100.0; - } - - // clear these out so we don't find them again and we know which ones - // we have left over - methods1[i] = nullptr; - methods2[match] = nullptr; - ptr++; - - matches++; - } - } - ptr->method1 = nullptr; - ptr->method2 = nullptr; - - qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesExculsive); - ptr = diffs; - - if (gOptions.outputHtml) { - printf(htmlHeader, gOptions.sortableUrl); - printf("

Table of Contents

\n"); - printf("\n"); - printf("Run 1: %s
\n", gOptions.diffFileName); - printf("Run 2: %s
\n", gOptions.traceFileName); - printf("

Exclusive

\n"); - printf(tableHeader, "exclusive_table"); - } - - char classBuf[HTML_BUFSIZE]; - char methodBuf[HTML_BUFSIZE]; - while (ptr->method1 != nullptr && ptr->method2 != nullptr) { - if (gOptions.outputHtml) printf("
\n"); - - char* className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE); - char* methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE); - - printf("%s.%s ", className, methodName); - if (gOptions.outputHtml) printf(""); - - printf("%" PRIu64 " ", ptr->method1->elapsedExclusive); - if (gOptions.outputHtml) printf(""); - - printf("%" PRIu64 " ", ptr->method2->elapsedExclusive); - if (gOptions.outputHtml) printf(""); - - printf("%" PRIu64 " ", ptr->differenceExclusive); - if (gOptions.outputHtml) printf(""); - - printf("%.2f\n", ptr->differenceExclusivePercentage); - if (gOptions.outputHtml) printf("\n"); - - printf("%d\n", ptr->method1->numCalls[0]); - if (gOptions.outputHtml) printf("\n"); - - printf("%d\n", ptr->method2->numCalls[0]); - if (gOptions.outputHtml) printf("
\n"); - - if (gOptions.outputHtml) { - printf(htmlHeader, gOptions.sortableUrl); - printf("Run 1: %s
\n", gOptions.diffFileName); - printf("Run 2: %s
\n", gOptions.traceFileName); - printf("

Inclusive

\n"); - printf(tableHeader, "inclusive_table"); - } - - qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesInculsive); - ptr = diffs; - - while (ptr->method1 != nullptr && ptr->method2 != nullptr) { - if (gOptions.outputHtml) printf("\n"); - - char* className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE); - char* methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE); - - printf("%s.%s ", className, methodName); - if (gOptions.outputHtml) printf(""); - - printf("%" PRIu64 " ", ptr->method1->elapsedInclusive); - if (gOptions.outputHtml) printf(""); - - printf("%" PRIu64 " ", ptr->method2->elapsedInclusive); - if (gOptions.outputHtml) printf(""); - - printf("%" PRIu64 " ", ptr->differenceInclusive); - if (gOptions.outputHtml) printf(""); - - printf("%.2f\n", ptr->differenceInclusivePercentage); - if (gOptions.outputHtml) printf("\n"); - - printf("%d\n", ptr->method1->numCalls[0]); - if (gOptions.outputHtml) printf("\n"); - - printf("%d\n", ptr->method2->numCalls[0]); - if (gOptions.outputHtml) printf("\n"); - - ptr++; - } - - if (gOptions.outputHtml) { - printf("\n"); - printf("

Run 1 methods not found in Run 2

"); - printf(tableHeaderMissing, "?"); - } - - for (int32_t i = 0; i < d1->numMethods; ++i) { - if (methods1[i] != nullptr) { - printMissingMethod(methods1[i]); - } - } - - if (gOptions.outputHtml) { - printf("\n"); - printf("

Run 2 methods not found in Run 1

"); - printf(tableHeaderMissing, "?"); - } - - for (int32_t i = 0; i < d2->numMethods; ++i) { - if (methods2[i] != nullptr) { - printMissingMethod(methods2[i]); - } - } - - if (gOptions.outputHtml) printf("numMethods, sumThreadTime); - if (gOptions.graphFileName != nullptr) { - createInclusiveProfileGraphNew(dataKeys); - } - free(methods); - } - - freeDataKeys(dataKeys); - - return 0; -} diff -Nru android-platform-dalvik-7.0.0+r33/debian/android-libdex-dev.install android-platform-dalvik-8.1.0+r23/debian/android-libdex-dev.install --- android-platform-dalvik-7.0.0+r33/debian/android-libdex-dev.install 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/android-libdex-dev.install 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -#!/usr/bin/dh-exec - -debian/out/libdex.so usr/lib/${DEB_HOST_MULTIARCH}/android -libdex/*.h usr/include/android/libdex \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/android-libdex.install android-platform-dalvik-8.1.0+r23/debian/android-libdex.install --- android-platform-dalvik-7.0.0+r33/debian/android-libdex.install 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/android-libdex.install 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -#!/usr/bin/dh-exec - -debian/out/libdex.so.0 usr/lib/${DEB_HOST_MULTIARCH}/android diff -Nru android-platform-dalvik-7.0.0+r33/debian/android-libdex.lintian-overrides android-platform-dalvik-8.1.0+r23/debian/android-libdex.lintian-overrides --- android-platform-dalvik-7.0.0+r33/debian/android-libdex.lintian-overrides 2016-07-27 11:48:09.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/android-libdex.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -# Android internal libraries are installed in a private directory - -binary-or-shlib-defines-rpath \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/android-platform-dalvik.docs android-platform-dalvik-8.1.0+r23/debian/android-platform-dalvik.docs --- android-platform-dalvik-7.0.0+r33/debian/android-platform-dalvik.docs 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/android-platform-dalvik.docs 2018-10-24 12:19:33.000000000 +0000 @@ -0,0 +1 @@ +NOTICE diff -Nru android-platform-dalvik-7.0.0+r33/debian/build.gradle android-platform-dalvik-8.1.0+r23/debian/build.gradle --- android-platform-dalvik-7.0.0+r33/debian/build.gradle 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/build.gradle 2018-10-24 12:19:33.000000000 +0000 @@ -1,13 +1,8 @@ apply plugin: 'java' -apply plugin: 'maven' -version = DX_VERSION +version = System.getenv('DEB_VERSION_UPSTREAM').split('\\+')[0] group = 'com.android' -dependencies { - compile 'com.android:dex:debian' -} - sourceSets { main { java { srcDir '../dx/src' } @@ -17,17 +12,7 @@ jar { manifest { from '../dx/etc/manifest.txt' - attributes 'Class-Path': '/usr/share/java/com.android.dex.jar' - } -} - -task jackDxPom { - outputs.files "${buildDir}/debian/jack-dx.pom" - doFirst { - pom { - project { groupId 'com.android.jack'} - }.writeTo("${buildDir}/debian/jack-dx.pom") } } -defaultTasks 'assemble', 'jackDxPom' \ No newline at end of file +defaultTasks 'assemble' \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/changelog android-platform-dalvik-8.1.0+r23/debian/changelog --- android-platform-dalvik-7.0.0+r33/debian/changelog 2017-04-26 18:00:06.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/changelog 2019-03-11 14:29:31.000000000 +0000 @@ -1,3 +1,39 @@ +android-platform-dalvik (8.1.0+r23-2~18.04) bionic; urgency=medium + + * Backport for OpenJDK 11. LP: #1819448. + + -- Matthias Klose Mon, 11 Mar 2019 15:29:31 +0100 + +android-platform-dalvik (8.1.0+r23-2) unstable; urgency=medium + + * Install manpages pre-baked by pandoc. Pandoc as a B-D is thus unnecessary. + * Fix FTCBFS by using dh_auto_build to invoke makefiles + * Remove unused build-dependency libjarajr-java + + -- Kai-Chung Yan Wed, 24 Oct 2018 21:32:58 +0800 + +android-platform-dalvik (8.1.0+r23-1) unstable; urgency=medium + + [ Hans-Christoph Steiner ] + * New upstream release + * Switch to Salsa + * Remove get-orig-source + * Fix missing-notice-file-for-apache-license + + [ Kai-Chung Yan ] + * Standards-Version => 4.2.1 + * No longer build dmtracedump or dexdump in this package. They have been moved + to android-platform-art. + * Annotate Multi-Arch on all packages + * Build hprof-conv on all architectures + * dalvik-exchange: + * Use the package upstream version as the JAR version + * No longer build against dex.jar (Closes: #911103) + * Fix some literal errors in the package description + * Install mainDexClassesNoAapt.rules + + -- Kai-Chung Yan Wed, 24 Oct 2018 20:28:49 +0800 + android-platform-dalvik (7.0.0+r33-1) unstable; urgency=medium * New upstream release diff -Nru android-platform-dalvik-7.0.0+r33/debian/clean android-platform-dalvik-8.1.0+r23/debian/clean --- android-platform-dalvik-7.0.0+r33/debian/clean 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/clean 2018-10-24 12:19:33.000000000 +0000 @@ -0,0 +1,2 @@ +debian/.gradle/ +debian/out/ \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/control android-platform-dalvik-8.1.0+r23/debian/control --- android-platform-dalvik-7.0.0+r33/debian/control 2017-04-26 18:00:06.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/control 2018-10-24 13:30:16.000000000 +0000 @@ -4,70 +4,19 @@ Maintainer: Android Tools Maintainers Uploaders: Kai-Chung Yan , Hans-Christoph Steiner -Build-Depends: android-libcutils-dev (>= 1:7.0.0+r33~) [amd64 i386 armel armhf arm64 mips mipsel mips64el], - android-liblog-dev (>= 1:7.0.0+r33~) [amd64 i386 armel armhf arm64 mips mipsel mips64el], - android-libnativehelper-dev (>= 7.0.0+r1) [amd64 i386 armel armhf arm64 mips mipsel mips64el], - android-libutils-dev (>= 1:7.0.0+r33~) [amd64 i386 armel armhf arm64 mips mipsel mips64el], - android-libziparchive-dev (>= 1:7.0.0+r33~) [amd64 i386 armel armhf arm64 mips mipsel mips64el], - debhelper (>= 10), +Build-Depends: debhelper (>= 10), dh-exec, gradle-debian-helper (>= 1.4~), - libsafe-iop-dev [amd64 i386 armel armhf arm64 mips mipsel mips64el], - maven-repo-helper, - pandoc, - zlib1g-dev [amd64 i386 armel armhf arm64 mips mipsel mips64el] -Build-Depends-Indep: default-jdk-headless | default-jdk, - libandroid-dex-java (>= 7.0.0+r33~), - libjarjar-java -Standards-Version: 3.9.8 + maven-repo-helper +Build-Depends-Indep: default-jdk-headless | default-jdk +Standards-Version: 4.2.1 Homepage: https://android.googlesource.com/platform/dalvik -Vcs-Git: https://anonscm.debian.org/git/android-tools/android-platform-dalvik.git -Vcs-Browser: https://anonscm.debian.org/cgit/android-tools/android-platform-dalvik.git - -Package: android-libdex -Section: libs -Architecture: amd64 i386 armel armhf arm64 mips mipsel mips64el -Depends: ${shlibs:Depends}, ${misc:Depends} -Description: Dalvik Executable library - Dalvik is a process virtual machine in Google's Android operating system that - executes applications written for Android. - . - This library provides APIs for Dalvik Executables. - -Package: android-libdex-dev -Section: libdevel -Architecture: amd64 i386 armel armhf arm64 mips mipsel mips64el -Depends: android-libdex (= ${binary:Version}), - android-libnativehelper-dev, - ${misc:Depends} -Breaks: android-libdex (<< 7.0.0+r1-2~) -Replaces: android-libdex (<< 7.0.0+r1-2~) -Description: Dalvik Executable library - Development files - Dalvik is a process virtual machine in Google's Android operating system that - executes applications written for Android. - . - This library provides APIs for Dalvik Executables. - . - This package contains the development files. - -Package: dexdump -Architecture: amd64 i386 armel armhf arm64 mips mipsel mips64el -Depends: ${misc:Depends}, ${shlibs:Depends} -Description: Tool to display information about Android .dex files - The "dexdump" tool is intended to mimic "objdump". When possible, use similar - command-line arguments. - . - Note: dexdump on amd64 will not work until the upstream fixes it. - -Package: dmtracedump -Architecture: amd64 i386 armel armhf arm64 mips mipsel mips64el -Depends: ${shlibs:Depends}, ${misc:Depends} -Description: Java method trace dump tool - dmtracedump is a tool that gives you an alternate way of generating graphical - call-stack diagrams from trace log files (instead of using Traceview). +Vcs-Git: https://salsa.debian.org/android-tools-team/android-platform-dalvik.git +Vcs-Browser: https://salsa.debian.org/android-tools-team/android-platform-dalvik Package: hprof-conv -Architecture: amd64 i386 armel armhf arm64 mips mipsel mips64el +Architecture: any +Multi-Arch: foreign Depends: ${shlibs:Depends}, ${misc:Depends} Description: HPROF Converter The hprof-conv tool converts the HPROF file that is generated by the Android @@ -76,8 +25,8 @@ Package: dalvik-exchange Architecture: all +Multi-Arch: foreign Depends: default-jre-headless (>= 1:1.6) | java6-runtime-headless, - libandroid-dex-java (>= 7.0.0+r33~), libandroid-23-java (>= 6.0.1+r72-3~), proguard-cli, ${misc:Depends} @@ -85,9 +34,9 @@ Dalvik is a process virtual machine in Google's Android operating system that executes applications written for Android. . - dalvik-exchange is the home of Dalvik eXchange, the thing that takes in class + dalvik-exchange is the tool that takes in class files and reformulates them for consumption in the VM. It also does a few other - things; use "dalvik-exchange --help" to see a modicum of self-documentation. + things. . This program is officially named "dx" but in order to avoid naming conflict with OpenDX in Debian it is renamed to "dalvik-exchange". diff -Nru android-platform-dalvik-7.0.0+r33/debian/copyright android-platform-dalvik-8.1.0+r23/debian/copyright --- android-platform-dalvik-7.0.0+r33/debian/copyright 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/copyright 2018-10-24 12:25:36.000000000 +0000 @@ -1,9 +1,8 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0 Source: https://android.googlesource.com/platform/dalvik -Files-Excluded: *.class *.jar +Files-Excluded: */jasmin.jar Files: * - debian/additionalSrc/dmtracedump/* Copyright: 2005-2015, The Android Open Source Project 2006, Google Inc. 2005-2006, Intel Corporation diff -Nru android-platform-dalvik-7.0.0+r33/debian/dalvik-exchange.docs android-platform-dalvik-8.1.0+r23/debian/dalvik-exchange.docs --- android-platform-dalvik-7.0.0+r33/debian/dalvik-exchange.docs 2016-02-18 08:23:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dalvik-exchange.docs 2018-10-24 12:19:33.000000000 +0000 @@ -1 +1,2 @@ -dx/README.txt \ No newline at end of file +dx/README.txt +NOTICE diff -Nru android-platform-dalvik-7.0.0+r33/debian/dalvik-exchange.install android-platform-dalvik-8.1.0+r23/debian/dalvik-exchange.install --- android-platform-dalvik-7.0.0+r33/debian/dalvik-exchange.install 2017-03-10 21:13:31.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dalvik-exchange.install 2018-10-24 12:19:33.000000000 +0000 @@ -1,3 +1,4 @@ dx/etc/dx usr/lib/android-sdk/build-tools/debian dx/etc/mainDexClasses usr/lib/android-sdk/build-tools/debian -dx/etc/mainDexClasses.rules usr/lib/android-sdk/build-tools/debian \ No newline at end of file +dx/etc/mainDexClasses.rules usr/lib/android-sdk/build-tools/debian +dx/etc/mainDexClassesNoAapt.rules usr/lib/android-sdk/build-tools/debian \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/dexdump.1.md android-platform-dalvik-8.1.0+r23/debian/dexdump.1.md --- android-platform-dalvik-7.0.0+r33/debian/dexdump.1.md 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dexdump.1.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -% DEXDUMP(1) android-platform-dalvik | dexdump Manuals -% The Android Open Source Project -% 7.0.0_r1 - -# NAME - -dexdump - Display information about Android .dex files - -# SYNOPSIS - -**dexdump** [-c] [-d] [-f] [-h] [-i] [-l _layout_] [-m] [-t _tempfile_] _dexfile_... - -# DESCRIPTION - -**dexdump** is intended to mimic **objdump**. When possible, use similar -command-line arguments. - -# OPTIONS - --c -: Verify checksum and exit. - --d -: Disassemble code sections. - --f -: Display summary information from file header. - --h -: Display file header details. - --i -: Ignore checksum failures. - --l -: Output layout, either **plain** or **xml**. - --m -: Dump register maps (and nothing else). - --t -: Temp file name (defaults to **/sdcard/dex-temp-\***). \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/dexdump.install android-platform-dalvik-8.1.0+r23/debian/dexdump.install --- android-platform-dalvik-7.0.0+r33/debian/dexdump.install 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dexdump.install 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -debian/out/dexdump usr/lib/android-sdk/build-tools/debian \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/dexdump.links android-platform-dalvik-8.1.0+r23/debian/dexdump.links --- android-platform-dalvik-7.0.0+r33/debian/dexdump.links 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dexdump.links 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -usr/lib/android-sdk/build-tools/debian/dexdump usr/bin/dexdump \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/dexdump.lintian-overrides android-platform-dalvik-8.1.0+r23/debian/dexdump.lintian-overrides --- android-platform-dalvik-7.0.0+r33/debian/dexdump.lintian-overrides 2016-07-27 11:48:09.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dexdump.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -# Android internal libraries are installed in a private directory - -binary-or-shlib-defines-rpath \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/dexdump.manpages android-platform-dalvik-8.1.0+r23/debian/dexdump.manpages --- android-platform-dalvik-7.0.0+r33/debian/dexdump.manpages 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dexdump.manpages 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -debian/out/dexdump.1 \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/dexdump.mk android-platform-dalvik-8.1.0+r23/debian/dexdump.mk --- android-platform-dalvik-7.0.0+r33/debian/dexdump.mk 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dexdump.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -NAME = dexdump -SOURCES = DexDump.cpp -SOURCES := $(foreach source, $(SOURCES), dexdump/$(source)) -CPPFLAGS += -I/usr/include/android -I. -LDFLAGS += -Wl,-rpath=/usr/lib/$(DEB_HOST_MULTIARCH)/android -Ldebian/out -ldex - -build: $(SOURCES) - mkdir --parents debian/out - $(CXX) $^ -o debian/out/$(NAME) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/dmtracedump.1.md android-platform-dalvik-8.1.0+r23/debian/dmtracedump.1.md --- android-platform-dalvik-7.0.0+r33/debian/dmtracedump.1.md 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dmtracedump.1.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -% DMTRACEDUMP(1) android-platform-dalvik | dmtracedump Manuals -% The Android Open Source Project -% 7.0.0_r1 - -# NAME - -dmtracedump - Java method trace dump tool - -# SYNOPSIS - -**dmtracedump** [-h] [-o] [-s _sortable_] [-d _trace-file-name_] [-g _outfile_] _trace-file-name_ - -# DESCRIPTION - -**dmtracedump** is a tool that gives you an alternate way of generating -graphical call-stack diagrams from trace log files (instead of using Traceview). - -The tool loads trace log data from **_trace-file-name_.data** and -**_trace-file-name_.key**. - -# OPTIONS - --d _trace-file-name_ -: Diff with this trace - --g _outfile_ -: Write graph to _outfile_ - --k -: When writing a graph, keep the intermediate DOT file - --h -: Turn on HTML output - --o -: Dump the dmtrace file instead of profiling - --s -: URL base to where the sortable JavaScript file - --t _threshold_ -: Threshold percentage for including nodes in the graph - -# SEE ALSO - -https://developer.android.com/tools/help/dmtracedump.html \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/dmtracedump.install android-platform-dalvik-8.1.0+r23/debian/dmtracedump.install --- android-platform-dalvik-7.0.0+r33/debian/dmtracedump.install 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dmtracedump.install 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -debian/out/dmtracedump usr/lib/android-sdk/platform-tools \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/dmtracedump.links android-platform-dalvik-8.1.0+r23/debian/dmtracedump.links --- android-platform-dalvik-7.0.0+r33/debian/dmtracedump.links 2016-02-18 08:23:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dmtracedump.links 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -usr/lib/android-sdk/platform-tools/dmtracedump usr/bin/dmtracedump \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/dmtracedump.manpages android-platform-dalvik-8.1.0+r23/debian/dmtracedump.manpages --- android-platform-dalvik-7.0.0+r33/debian/dmtracedump.manpages 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dmtracedump.manpages 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -debian/out/dmtracedump.1 \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/dmtracedump.mk android-platform-dalvik-8.1.0+r23/debian/dmtracedump.mk --- android-platform-dalvik-7.0.0+r33/debian/dmtracedump.mk 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/dmtracedump.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -NAME = dmtracedump -SOURCES = tracedump.cc -SOURCES := $(foreach source, $(SOURCES), debian/additionalSrc/dmtracedump/$(source)) -CPPFLAGS += -Idebian/additionalSrc/dmtracedump - -build: $(SOURCES) - mkdir --parents debian/out - $(CXX) $^ -o debian/out/$(NAME) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/.gitlab-ci.yml android-platform-dalvik-8.1.0+r23/debian/.gitlab-ci.yml --- android-platform-dalvik-7.0.0+r33/debian/.gitlab-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/.gitlab-ci.yml 2018-10-24 12:19:33.000000000 +0000 @@ -0,0 +1,11 @@ +image: registry.gitlab.com/eighthave/ci-image-git-buildpackage:latest + +build: + artifacts: + paths: + - "*.deb" + expire_in: 1 day + script: + # copy it into place to be saved as an artifact + - cp ../*.deb . + - apt-get -qy install ../*.deb diff -Nru android-platform-dalvik-7.0.0+r33/debian/hprof-conv.1 android-platform-dalvik-8.1.0+r23/debian/hprof-conv.1 --- android-platform-dalvik-7.0.0+r33/debian/hprof-conv.1 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/hprof-conv.1 2018-10-24 13:14:13.000000000 +0000 @@ -0,0 +1,26 @@ +.\" Automatically generated by Pandoc 2.2.1 +.\" +.TH "HPROF\-CONV" "1" "" "" "" +.hy +.SH NAME +.PP +hprof\-conv \- HPROF Converter +.SH SYNOPSIS +.PP +\f[B]hprof\-conf\f[] [\-z] \f[I]infile\f[] \f[I]outfile\f[] +.SH DESCRIPTION +.PP +The \f[B]hprof\-conv\f[] tool converts the HPROF file that is generated +by the Android SDK tools to a standard format so you can view the file +in a profiling tool of your choice. +.SH OPTIONS +.TP +.B \-z +Exclude non\-app heaps, such as Zygote +.RS +.RE +.PP +Use \f[B]\-\f[] for \f[I]infile\f[] or \f[I]outfile\f[] to specify +\f[B]stdin\f[] or \f[B]stdout\f[]. +.SH AUTHORS +The Android Open Source Project. diff -Nru android-platform-dalvik-7.0.0+r33/debian/hprof-conv.1.md android-platform-dalvik-8.1.0+r23/debian/hprof-conv.1.md --- android-platform-dalvik-7.0.0+r33/debian/hprof-conv.1.md 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/hprof-conv.1.md 2018-10-24 12:19:33.000000000 +0000 @@ -1,6 +1,5 @@ -% HPROF-CONV(1) android-platform-dalvik | hprof-conv Manuals +% HPROF-CONV(1) % The Android Open Source Project -% 7.0.0_r1 # NAME @@ -19,10 +18,6 @@ # OPTIONS -z -: Filter out zygote allocations shared by all applications. +: Exclude non-app heaps, such as Zygote -You can use **-** for _infile_ or _outfile_ to specify **stdin** or **stdout**. - -# SEE ALSO - -https://developer.android.com/tools/help/hprof-conv.html \ No newline at end of file +Use **-** for _infile_ or _outfile_ to specify **stdin** or **stdout**. \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/hprof-conv.docs android-platform-dalvik-8.1.0+r23/debian/hprof-conv.docs --- android-platform-dalvik-7.0.0+r33/debian/hprof-conv.docs 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/hprof-conv.docs 2018-10-24 12:19:33.000000000 +0000 @@ -0,0 +1 @@ +NOTICE diff -Nru android-platform-dalvik-7.0.0+r33/debian/hprof-conv.manpages android-platform-dalvik-8.1.0+r23/debian/hprof-conv.manpages --- android-platform-dalvik-7.0.0+r33/debian/hprof-conv.manpages 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/hprof-conv.manpages 2018-10-24 13:15:02.000000000 +0000 @@ -1 +1 @@ -debian/out/hprof-conv.1 \ No newline at end of file +debian/hprof-conv.1 \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/jack-dx.jarjar-rules android-platform-dalvik-8.1.0+r23/debian/jack-dx.jarjar-rules --- android-platform-dalvik-7.0.0+r33/debian/jack-dx.jarjar-rules 2016-08-22 10:26:08.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/jack-dx.jarjar-rules 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -rule com.android.dx.** com.android.jack.dx.@1 -zap com.android.dx.command.** -zap com.android.dx.dex.cf.** -zap com.android.multidex.** \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/libandroid-jack-dx-java.poms android-platform-dalvik-8.1.0+r23/debian/libandroid-jack-dx-java.poms --- android-platform-dalvik-7.0.0+r33/debian/libandroid-jack-dx-java.poms 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/libandroid-jack-dx-java.poms 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -debian/build/debian/jack-dx.pom --artifact=debian/build/libs/jack-dx.jar --java-lib --usj-name=com.android.jack.dx diff -Nru android-platform-dalvik-7.0.0+r33/debian/libdex.mk android-platform-dalvik-8.1.0+r23/debian/libdex.mk --- android-platform-dalvik-7.0.0+r33/debian/libdex.mk 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/libdex.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -NAME = libdex -SOURCES = CmdUtils.cpp \ - DexCatch.cpp \ - DexClass.cpp \ - DexDataMap.cpp \ - DexDebugInfo.cpp \ - DexFile.cpp \ - DexInlines.cpp \ - DexOptData.cpp \ - DexOpcodes.cpp \ - DexProto.cpp \ - DexSwapVerify.cpp \ - DexUtf.cpp \ - InstrUtils.cpp \ - Leb128.cpp \ - OptInvocation.cpp \ - sha1.cpp \ - SysUtil.cpp -SOURCES := $(foreach source, $(SOURCES), libdex/$(source)) -CPPFLAGS += -I/usr/include/android -I/usr/include/android/nativehelper -I. -LDFLAGS += -shared -Wl,-rpath=/usr/lib/$(DEB_HOST_MULTIARCH)/android -Wl,-soname,$(NAME).so.0 \ - -lz -L/usr/lib/$(DEB_HOST_MULTIARCH)/android -llog -lziparchive - -build: $(SOURCES) - mkdir --parents debian/out - $(CXX) $^ -o debian/out/$(NAME).so.0 $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) - ln -s $(NAME).so.0 debian/out/$(NAME).so \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/patches/mainDexclasses_uses_local_artifacts.patch android-platform-dalvik-8.1.0+r23/debian/patches/mainDexclasses_uses_local_artifacts.patch --- android-platform-dalvik-7.0.0+r33/debian/patches/mainDexclasses_uses_local_artifacts.patch 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/patches/mainDexclasses_uses_local_artifacts.patch 2018-10-24 12:19:33.000000000 +0000 @@ -3,7 +3,7 @@ Last-Update: 2016-12-07 --- a/dx/etc/mainDexClasses +++ b/dx/etc/mainDexClasses -@@ -83,7 +83,7 @@ +@@ -89,7 +89,7 @@ fi proguardExec="proguard.sh" diff -Nru android-platform-dalvik-7.0.0+r33/debian/README.source android-platform-dalvik-8.1.0+r23/debian/README.source --- android-platform-dalvik-7.0.0+r33/debian/README.source 2016-12-07 15:17:56.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/README.source 2018-10-24 12:19:33.000000000 +0000 @@ -9,15 +9,4 @@ For more info: https://wiki.debian.org/AndroidTools#Updating_the_source_packages -debian/additionalSrc/dmtracedump/* -================================== - -These files are copied from [1]. dmtracedump was moved to android-platform-art -since Android 7.0.0, but at the time we don't have enough time to get another -NEW package accepted (Stretch freeze is near!), therefore we include the source -here. - -[1]: https://android.googlesource.com/platform/art/+/master/tools/dmtracedump - - -- Hans-Christoph Steiner , Thu, 18 Feb 2016 09:39:38 +0100 - -- Kai-Chung Yan Tue, 06 Dec 2016 13:41:48 +0800 \ No newline at end of file + -- Hans-Christoph Steiner , Thu, 18 Feb 2016 09:39:38 +0100 \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/debian/rules android-platform-dalvik-8.1.0+r23/debian/rules --- android-platform-dalvik-7.0.0+r33/debian/rules 2017-03-10 21:13:31.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/debian/rules 2018-10-24 13:29:17.000000000 +0000 @@ -3,63 +3,32 @@ include /usr/share/dpkg/architecture.mk include /usr/share/dpkg/pkg-info.mk +export DEB_VERSION_UPSTREAM export DEB_HOST_MULTIARCH export DEB_CFLAGS_MAINT_APPEND = -fPIC -DNDEBUG export DEB_CXXFLAGS_MAINT_APPEND = -fPIC -DNDEBUG export DEB_LDFLAGS_MAINT_APPEND = -fPIC -# See com/android/dx/Version.java -DX_VERSION = 1.12 -ARCH_COMPONENTS = libdex.so dexdump dmtracedump hprof-conv - -# If building on architectures other than the supported ones, only build the -# architecture independent components. -ifeq ($(filter amd64 i386 armel armhf arm64 mips mipsel mips64el,$(DEB_HOST_ARCH)),) - ARCH_COMPONENTS = -endif debian/out/lib%.so: debian/lib%.mk - make -f $< + dh_auto_build --buildsystem=makefile -- --file=$< debian/out/%: debian/%.mk - make -f $< - pandoc -s -o debian/out/$(subst debian/out/,,$@).1 debian/$(subst debian/out/,,$@).1.md - -debian/out/dexdump: debian/dexdump.mk debian/out/libdex.so - make -f $< - pandoc -s -o debian/out/dexdump.1 debian/dexdump.1.md + dh_auto_build --buildsystem=makefile -- --file=$< %: dh $@ override_dh_auto_build-indep: - mkdir --parents debian/out dh_auto_build --buildsystem=gradle -- \ - --settings-file debian/settings.gradle \ - --project-prop DX_VERSION=$(DX_VERSION) - java -jar /usr/share/java/jarjar.jar \ - process \ - debian/jack-dx.jarjar-rules \ - debian/build/libs/dx-$(DX_VERSION).jar \ - debian/build/libs/jack-dx.jar + --settings-file debian/settings.gradle override_dh_auto_clean-indep: dh_auto_clean --indep --buildsystem=gradle mh_clean - $(RM) -r debian/.gradle debian/out - -override_dh_auto_build-arch: $(foreach component,$(ARCH_COMPONENTS),debian/out/$(component)) -override_dh_auto_clean-arch: - dh_auto_clean --arch - $(RM) -r debian/out +override_dh_auto_build-arch: debian/out/hprof-conv override_dh_install-indep: dh_install --indep chmod +x debian/dalvik-exchange/usr/lib/android-sdk/build-tools/debian/dx - mh_install - -override_dh_shlibdeps: - dh_shlibdeps -l/usr/lib/$(DEB_HOST_MULTIARCH)/android - -get-orig-source: - uscan --download-current-version --force-download \ No newline at end of file + mh_install \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/dexdump/DexDump.cpp android-platform-dalvik-8.1.0+r23/dexdump/DexDump.cpp --- android-platform-dalvik-7.0.0+r33/dexdump/DexDump.cpp 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dexdump/DexDump.cpp 2018-10-24 12:07:51.000000000 +0000 @@ -81,6 +81,13 @@ const char* signature; }; + +/* basic info about a prototype */ +struct ProtoInfo { + char* parameterTypes; // dynamically allocated with malloc + const char* returnType; +}; + /* * Get 2 little-endian bytes. */ @@ -691,6 +698,63 @@ return true; } +/* + * Get information about a ProtoId. + */ +bool getProtoInfo(DexFile* pDexFile, u4 protoIdx, ProtoInfo* pProtoInfo) +{ + if (protoIdx >= pDexFile->pHeader->protoIdsSize) { + return false; + } + + const DexProtoId* protoId = dexGetProtoId(pDexFile, protoIdx); + + // Get string for return type. + if (protoId->returnTypeIdx >= pDexFile->pHeader->typeIdsSize) { + return false; + } + pProtoInfo->returnType = dexStringByTypeIdx(pDexFile, protoId->returnTypeIdx); + + // Build string for parameter types. + size_t bufSize = 1; + char* buf = (char*)malloc(bufSize); + if (buf == NULL) { + return false; + } + + buf[0] = '\0'; + size_t bufUsed = 1; + + const DexTypeList* paramTypes = dexGetProtoParameters(pDexFile, protoId); + if (paramTypes == NULL) { + // No parameters. + pProtoInfo->parameterTypes = buf; + return true; + } + + for (u4 i = 0; i < paramTypes->size; ++i) { + if (paramTypes->list[i].typeIdx >= pDexFile->pHeader->typeIdsSize) { + free(buf); + return false; + } + const char* param = dexStringByTypeIdx(pDexFile, paramTypes->list[i].typeIdx); + size_t newUsed = bufUsed + strlen(param); + if (newUsed > bufSize) { + char* newBuf = (char*)realloc(buf, newUsed); + if (newBuf == NULL) { + free(buf); + return false; + } + buf = newBuf; + bufSize = newUsed; + } + strncat(buf + bufUsed - 1, param, bufSize - (bufUsed - 1)); + bufUsed = newUsed; + } + + pProtoInfo->parameterTypes = buf; + return true; +} /* * Look up a class' descriptor. @@ -709,11 +773,16 @@ * this can be compared with the one passed in, to see if the result * needs to be free()d. */ -static char* indexString(DexFile* pDexFile, - const DecodedInstruction* pDecInsn, char* buf, size_t bufSize) +static char* indexString(DexFile* pDexFile, const DecodedInstruction* pDecInsn, size_t bufSize) { + char* buf = (char*)malloc(bufSize); + if (buf == NULL) { + return NULL; + } + int outSize; u4 index; + u4 secondaryIndex = 0; u4 width; /* TODO: Make the index *always* be in field B, to simplify this code. */ @@ -738,6 +807,12 @@ index = pDecInsn->vC; width = 4; break; + case kFmt45cc: + case kFmt4rcc: + index = pDecInsn->vB; // method index + secondaryIndex = pDecInsn->arg[4]; // proto index + width = 4; + break; default: index = 0; width = 4; @@ -822,6 +897,27 @@ case kIndexFieldOffset: outSize = snprintf(buf, bufSize, "[obj+%0*x]", width, index); break; + case kIndexMethodAndProtoRef: + { + FieldMethodInfo methInfo; + ProtoInfo protoInfo; + protoInfo.parameterTypes = NULL; + if (getMethodInfo(pDexFile, index, &methInfo) && + getProtoInfo(pDexFile, secondaryIndex, &protoInfo)) { + outSize = snprintf(buf, bufSize, "%s.%s:%s, (%s)%s // method@%0*x, proto@%0*x", + methInfo.classDescriptor, methInfo.name, methInfo.signature, + protoInfo.parameterTypes, protoInfo.returnType, + width, index, width, secondaryIndex); + } else { + outSize = snprintf(buf, bufSize, ", // method@%0*x, proto@%0*x", + width, index, width, secondaryIndex); + } + free(protoInfo.parameterTypes); + } + break; + case kCallSiteRef: + outSize = snprintf(buf, bufSize, "call_site@%0*x", width, index); + break; default: outSize = snprintf(buf, bufSize, ""); break; @@ -833,12 +929,8 @@ * snprintf() doesn't count the '\0' as part of its returned * size, so we add explicit space for it here. */ - outSize++; - buf = (char*)malloc(outSize); - if (buf == NULL) { - return NULL; - } - return indexString(pDexFile, pDecInsn, buf, outSize); + free(buf); + return indexString(pDexFile, pDecInsn, outSize + 1); } else { return buf; } @@ -850,8 +942,6 @@ void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx, int insnWidth, const DecodedInstruction* pDecInsn) { - char indexBufChars[200]; - char *indexBuf = indexBufChars; const u2* insns = pCode->insns; int i; @@ -890,9 +980,11 @@ printf("|%04x: %s", insnIdx, dexGetOpcodeName(pDecInsn->opcode)); } + // Provide an initial buffer that usually suffices, although indexString() + // may reallocate the buffer if more space is needed. + char* indexBuf = NULL; if (pDecInsn->indexType != kIndexNone) { - indexBuf = indexString(pDexFile, pDecInsn, - indexBufChars, sizeof(indexBufChars)); + indexBuf = indexString(pDexFile, pDecInsn, 200); } switch (dexGetFormatFromOpcode(pDecInsn->opcode)) { @@ -1042,6 +1134,26 @@ break; case kFmt00x: // unknown op or breakpoint break; + case kFmt45cc: + { + fputs(" {", stdout); + printf("v%d", pDecInsn->vC); + for (int i = 0; i < (int) pDecInsn->vA - 1; ++i) { + printf(", v%d", pDecInsn->arg[i]); + } + printf("}, %s", indexBuf); + } + break; + case kFmt4rcc: + { + fputs(" {", stdout); + printf("v%d", pDecInsn->vC); + for (int i = 1; i < (int) pDecInsn->vA; ++i) { + printf(", v%d", pDecInsn->vC + i); + } + printf("}, %s", indexBuf); + } + break; default: printf(" ???"); break; @@ -1049,9 +1161,7 @@ putchar('\n'); - if (indexBuf != indexBufChars) { - free(indexBuf); - } + free(indexBuf); } /* @@ -1735,6 +1845,253 @@ } } +static const DexMapItem* findMapItem(const DexFile* pDexFile, u4 type) +{ + const u4 offset = pDexFile->pHeader->mapOff; + const DexMapList* list = (const DexMapList*)(pDexFile->baseAddr + offset); + for (u4 i = 0; i < list->size; ++i) { + if (list->list[i].type == type) { + return &list->list[i]; + } + } + return nullptr; +} + +static void dumpMethodHandles(DexFile* pDexFile) +{ + const DexMapItem* item = findMapItem(pDexFile, kDexTypeMethodHandleItem); + if (item == nullptr) return; + const DexMethodHandleItem* method_handles = + (const DexMethodHandleItem*)(pDexFile->baseAddr + item->offset); + for (u4 i = 0; i < item->size; ++i) { + const DexMethodHandleItem& mh = method_handles[i]; + const char* type; + bool is_invoke; + bool is_static; + switch ((MethodHandleType) mh.methodHandleType) { + case MethodHandleType::STATIC_PUT: + type = "put-static"; + is_invoke = false; + is_static = true; + break; + case MethodHandleType::STATIC_GET: + type = "get-static"; + is_invoke = false; + is_static = true; + break; + case MethodHandleType::INSTANCE_PUT: + type = "put-instance"; + is_invoke = false; + is_static = false; + break; + case MethodHandleType::INSTANCE_GET: + type = "get-instance"; + is_invoke = false; + is_static = false; + break; + case MethodHandleType::INVOKE_STATIC: + type = "invoke-static"; + is_invoke = true; + is_static = true; + break; + case MethodHandleType::INVOKE_INSTANCE: + type = "invoke-instance"; + is_invoke = true; + is_static = false; + break; + case MethodHandleType::INVOKE_CONSTRUCTOR: + type = "invoke-constructor"; + is_invoke = true; + is_static = false; + break; + case MethodHandleType::INVOKE_DIRECT: + type = "invoke-direct"; + is_invoke = true; + is_static = false; + break; + case MethodHandleType::INVOKE_INTERFACE: + type = "invoke-interface"; + is_invoke = true; + is_static = false; + break; + default: + printf("Unknown method handle type 0x%02x, skipped.", mh.methodHandleType); + continue; + } + + FieldMethodInfo info; + if (is_invoke) { + if (!getMethodInfo(pDexFile, mh.fieldOrMethodIdx, &info)) { + printf("Unknown method handle target method@%04x, skipped.", mh.fieldOrMethodIdx); + continue; + } + } else { + if (!getFieldInfo(pDexFile, mh.fieldOrMethodIdx, &info)) { + printf("Unknown method handle target field@%04x, skipped.", mh.fieldOrMethodIdx); + continue; + } + } + + const char* instance = is_static ? "" : info.classDescriptor; + + if (gOptions.outputFormat == OUTPUT_XML) { + printf("\n"); + } else { + printf("Method Handle #%u:\n", i); + printf(" type : %s\n", type); + printf(" target : %s %s\n", info.classDescriptor, info.name); + printf(" target_type : %c%s%s\n", info.signature[0], instance, info.signature + 1); + } + } +} + +/* Helper for dumpCallSites(), which reads a 1- to 8- byte signed + * little endian value. */ +static u8 readSignedLittleEndian(const u1** pData, u4 size) { + const u1* data = *pData; + u8 result = 0; + u4 i; + + for (i = 0; i < size; i++) { + result = (result >> 8) | (((int64_t)*data++) << 56); + } + + result >>= (8 - size) * 8; + *pData = data; + return result; +} + +/* Helper for dumpCallSites(), which reads a 1- to 8- byte unsigned + * little endian value. */ +static u8 readUnsignedLittleEndian(const u1** pData, u4 size, bool fillOnRight = false) { + const u1* data = *pData; + u8 result = 0; + u4 i; + + for (i = 0; i < size; i++) { + result = (result >> 8) | (((u8)*data++) << 56); + } + + u8 oldResult = result; + if (!fillOnRight) { + result >>= (8u - size) * 8; + } + + *pData = data; + return result; +} + +static void dumpCallSites(DexFile* pDexFile) +{ + const DexMapItem* item = findMapItem(pDexFile, kDexTypeCallSiteIdItem); + if (item == nullptr) return; + const DexCallSiteId* ids = (const DexCallSiteId*)(pDexFile->baseAddr + item->offset); + for (u4 index = 0; index < item->size; ++index) { + bool doXml = (gOptions.outputFormat == OUTPUT_XML); + printf(doXml ? "\n" : "Call Site #%u // offset %u\n", + index, ids[index].callSiteOff); + const u1* data = pDexFile->baseAddr + ids[index].callSiteOff; + u4 count = readUnsignedLeb128(&data); + for (u4 i = 0; i < count; ++i) { + printf(doXml ? "> kDexAnnotationValueArgShift; + switch (valueType) { + case kDexAnnotationByte: { + printf(doXml ? "type=\"byte\" value=\"%d\"/>" : "%d (byte)", (int)*data++); + break; + } + case kDexAnnotationShort: { + printf(doXml ? "type=\"short\" value=\"%d\"/>" : "%d (short)", + (int) readSignedLittleEndian(&data, valueArg + 1)); + break; + } + case kDexAnnotationChar: { + printf(doXml ? "type=\"short\" value=\"%u\"/>" : "%u (char)", + (u2) readUnsignedLittleEndian(&data, valueArg + 1)); + break; + } + case kDexAnnotationInt: { + printf(doXml ? "type=\"int\" value=\"%d\"/>" : "%d (int)", + (int) readSignedLittleEndian(&data, valueArg + 1)); + break; + } + case kDexAnnotationLong: { + printf(doXml ? "type=\"long\" value=\"%" PRId64 "\"/>" : "%" PRId64 " (long)", + (int64_t) readSignedLittleEndian(&data, valueArg + 1)); + break; + } + case kDexAnnotationFloat: { + u4 rawValue = (u4) (readUnsignedLittleEndian(&data, valueArg + 1, true) >> 32); + printf(doXml ? "type=\"float\" value=\"%g\"/>" : "%g (float)", + *((float*) &rawValue)); + break; + } + case kDexAnnotationDouble: { + u8 rawValue = readUnsignedLittleEndian(&data, valueArg + 1, true); + printf(doXml ? "type=\"double\" value=\"%g\"/>" : "%g (double)", + *((double*) &rawValue)); + break; + } + case kDexAnnotationMethodType: { + u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1); + ProtoInfo protoInfo; + memset(&protoInfo, 0, sizeof(protoInfo)); + getProtoInfo(pDexFile, idx, &protoInfo); + printf(doXml ? "type=\"MethodType\" value=\"(%s)%s\"/>" : "(%s)%s (MethodType)", + protoInfo.parameterTypes, protoInfo.returnType); + free(protoInfo.parameterTypes); + break; + } + case kDexAnnotationMethodHandle: { + u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1); + printf(doXml ? "type=\"MethodHandle\" value=\"%u\"/>" : "%u (MethodHandle)", + idx); + break; + } + case kDexAnnotationString: { + u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1); + printf(doXml ? "type=\"String\" value=\"%s\"/>" : "%s (String)", + dexStringById(pDexFile, idx)); + break; + } + case kDexAnnotationType: { + u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1); + printf(doXml ? "type=\"Class\" value=\"%s\"/>" : "%s (Class)", + dexStringByTypeIdx(pDexFile, idx)); + break; + } + case kDexAnnotationNull: { + printf(doXml ? "type=\"null\" value=\"null\"/>" : "null (null)"); + break; + } + case kDexAnnotationBoolean: { + printf(doXml ? "type=\"boolean\" value=\"%s\"/>" : "%s (boolean)", + (valueArg & 1) == 0 ? "false" : "true"); + break; + } + default: + // Other types are not anticipated being reached here. + printf("Unexpected type found, bailing on call site info.\n"); + i = count; + break; + } + printf("\n"); + } + + if (doXml) { + printf("\n"); + } + } +} + /* * Dump the requested sections of the file. */ @@ -1768,6 +2125,9 @@ dumpClass(pDexFile, i, &package); } + dumpMethodHandles(pDexFile); + dumpCallSites(pDexFile); + /* free the last one allocated */ if (package != NULL) { printf("\n"); diff -Nru android-platform-dalvik-7.0.0+r33/dexgen/src/com/android/dexgen/dex/code/DalvOps.java android-platform-dalvik-8.1.0+r23/dexgen/src/com/android/dexgen/dex/code/DalvOps.java --- android-platform-dalvik-7.0.0+r33/dexgen/src/com/android/dexgen/dex/code/DalvOps.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dexgen/src/com/android/dexgen/dex/code/DalvOps.java 2018-10-24 12:07:51.000000000 +0000 @@ -281,8 +281,8 @@ public static final int UNUSED_F7 = 0xf7; public static final int UNUSED_F8 = 0xf8; public static final int UNUSED_F9 = 0xf9; - public static final int UNUSED_FA = 0xfa; - public static final int UNUSED_FB = 0xfb; + public static final int INVOKE_POLYMORPHIC = 0xfa; + public static final int INVOKE_POLYMORPHIC_RANGE = 0xfb; public static final int UNUSED_FC = 0xfc; public static final int UNUSED_FD = 0xfd; public static final int UNUSED_FE = 0xfe; diff -Nru android-platform-dalvik-7.0.0+r33/dexgen/src/com/android/dexgen/dex/code/Dops.java android-platform-dalvik-8.1.0+r23/dexgen/src/com/android/dexgen/dex/code/Dops.java --- android-platform-dalvik-7.0.0+r33/dexgen/src/com/android/dexgen/dex/code/Dops.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dexgen/src/com/android/dexgen/dex/code/Dops.java 2018-10-24 12:07:51.000000000 +0000 @@ -39,6 +39,8 @@ import com.android.dexgen.dex.code.form.Form32x; import com.android.dexgen.dex.code.form.Form35c; import com.android.dexgen.dex.code.form.Form3rc; +import com.android.dexgen.dex.code.form.Form45cc; +import com.android.dexgen.dex.code.form.Form4rcc; import com.android.dexgen.dex.code.form.Form51l; import com.android.dexgen.dex.code.form.SpecialFormat; @@ -496,6 +498,10 @@ new Dop(DalvOps.INVOKE_INTERFACE, DalvOps.INVOKE_INTERFACE, Form35c.THE_ONE, false, "invoke-interface"); + public static final Dop INVOKE_POLYMORPHIC = + new Dop(DalvOps.INVOKE_POLYMORPHIC, DalvOps.INVOKE_POLYMORPHIC, + Form45cc.THE_ONE, false, "invoke-polymorphic"); + public static final Dop INVOKE_VIRTUAL_RANGE = new Dop(DalvOps.INVOKE_VIRTUAL_RANGE, DalvOps.INVOKE_VIRTUAL, Form3rc.THE_ONE, false, "invoke-virtual/range"); @@ -516,6 +522,10 @@ new Dop(DalvOps.INVOKE_INTERFACE_RANGE, DalvOps.INVOKE_INTERFACE, Form3rc.THE_ONE, false, "invoke-interface/range"); + public static final Dop INVOKE_POLYMORPHIC_RANGE = + new Dop(DalvOps.INVOKE_POLYMORPHIC_RANGE, DalvOps.INVOKE_POLYMORPHIC, + Form4rcc.THE_ONE, false, "invoke-polymorphic/range"); + public static final Dop NEG_INT = new Dop(DalvOps.NEG_INT, DalvOps.NEG_INT, Form12x.THE_ONE, true, "neg-int"); diff -Nru android-platform-dalvik-7.0.0+r33/dexgen/src/com/android/dexgen/dex/code/form/Form45cc.java android-platform-dalvik-8.1.0+r23/dexgen/src/com/android/dexgen/dex/code/form/Form45cc.java --- android-platform-dalvik-7.0.0+r33/dexgen/src/com/android/dexgen/dex/code/form/Form45cc.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dexgen/src/com/android/dexgen/dex/code/form/Form45cc.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2017 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.dexgen.dex.code.form; + +import com.android.dexgen.dex.code.CstInsn; +import com.android.dexgen.dex.code.DalvInsn; +import com.android.dexgen.dex.code.InsnFormat; +import com.android.dexgen.rop.code.RegisterSpec; +import com.android.dexgen.rop.code.RegisterSpecList; +import com.android.dexgen.rop.cst.Constant; +import com.android.dexgen.rop.cst.CstMethodRef; +import com.android.dexgen.rop.cst.CstType; +import com.android.dexgen.rop.type.Type; +import com.android.dexgen.util.AnnotatedOutput; + +/** + * Instruction format {@code 45cc}. See the instruction format spec + * for details. + */ +public final class Form45cc extends InsnFormat { + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form45cc(); + + /** Maximal number of operands */ + private static final int MAX_NUM_OPS = 5; + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form45cc() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = explicitize(insn.getRegisters()); + return regListString(regs) + ", " + cstString(insn); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + if (noteIndices) { + return cstComment(insn); + } else { + return ""; + } + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 4; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + if (!(insn instanceof CstInsn)) { + return false; + } + + CstInsn ci = (CstInsn) insn; + int cpi = ci.getIndex(); + + if (! unsignedFitsInShort(cpi)) { + return false; + } + + Constant cst = ci.getConstant(); + if (!((cst instanceof CstMethodRef) || + (cst instanceof CstType))) { + return false; + } + + RegisterSpecList regs = ci.getRegisters(); + return (wordCount(regs) >= 0); + } + + /** {@inheritDoc} */ + @Override + public InsnFormat nextUp() { + return Form4rcc.THE_ONE; + } + + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + int cpi = ((CstInsn) insn).getIndex(); + RegisterSpecList regs = explicitize(insn.getRegisters()); + int sz = regs.size(); + int r0 = (sz > 0) ? regs.get(0).getReg() : 0; + int r1 = (sz > 1) ? regs.get(1).getReg() : 0; + int r2 = (sz > 2) ? regs.get(2).getReg() : 0; + int r3 = (sz > 3) ? regs.get(3).getReg() : 0; + int r4 = (sz > 4) ? regs.get(4).getReg() : 0; + + write(out, + opcodeUnit(insn, + makeByte(r4, sz)), // encode the fifth operand here + (short) cpi, + codeUnit(r0, r1, r2, r3)); + } + + /** + * Gets the number of words required for the given register list, where + * category-2 values count as two words. Return {@code -1} if the + * list requires more than five words or contains registers that need + * more than a nibble to identify them. + * + * @param regs {@code non-null;} the register list in question + * @return {@code >= -1;} the number of words required, or {@code -1} + * if the list couldn't possibly fit in this format + */ + private static int wordCount(RegisterSpecList regs) { + int sz = regs.size(); + + if (sz > MAX_NUM_OPS) { + // It can't possibly fit. + return -1; + } + + int result = 0; + + for (int i = 0; i < sz; i++) { + RegisterSpec one = regs.get(i); + result += one.getCategory(); + /* + * The check below adds (category - 1) to the register, to + * account for the fact that the second half of a + * category-2 register has to be represented explicitly in + * the result. + */ + if (!unsignedFitsInNibble(one.getReg() + one.getCategory() - 1)) { + return -1; + } + } + + return (result <= MAX_NUM_OPS) ? result : -1; + } + + /** + * Returns a register list which is equivalent to the given one, + * except that it splits category-2 registers into two explicit + * entries. This returns the original list if no modification is + * required + * + * @param orig {@code non-null;} the original list + * @return {@code non-null;} the list with the described transformation + */ + private static RegisterSpecList explicitize(RegisterSpecList orig) { + int wordCount = wordCount(orig); + int sz = orig.size(); + + if (wordCount == sz) { + return orig; + } + + RegisterSpecList result = new RegisterSpecList(wordCount); + int wordAt = 0; + + for (int i = 0; i < sz; i++) { + RegisterSpec one = orig.get(i); + result.set(wordAt, one); + if (one.getCategory() == 2) { + result.set(wordAt + 1, + RegisterSpec.make(one.getReg() + 1, Type.VOID)); + wordAt += 2; + } else { + wordAt++; + } + } + + result.setImmutable(); + return result; + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dexgen/src/com/android/dexgen/dex/code/form/Form4rcc.java android-platform-dalvik-8.1.0+r23/dexgen/src/com/android/dexgen/dex/code/form/Form4rcc.java --- android-platform-dalvik-7.0.0+r33/dexgen/src/com/android/dexgen/dex/code/form/Form4rcc.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dexgen/src/com/android/dexgen/dex/code/form/Form4rcc.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2017 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.dexgen.dex.code.form; + +import com.android.dexgen.dex.code.CstInsn; +import com.android.dexgen.dex.code.DalvInsn; +import com.android.dexgen.dex.code.InsnFormat; +import com.android.dexgen.rop.code.RegisterSpec; +import com.android.dexgen.rop.code.RegisterSpecList; +import com.android.dexgen.rop.cst.Constant; +import com.android.dexgen.rop.cst.CstMethodRef; +import com.android.dexgen.rop.cst.CstType; +import com.android.dexgen.util.AnnotatedOutput; + +/** + * Instruction format {@code 4rcc}. See the instruction format spec + * for details. + */ +public final class Form4rcc extends InsnFormat { + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form4rcc(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form4rcc() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int size = regs.size(); + StringBuilder sb = new StringBuilder(30); + + sb.append("{"); + + switch (size) { + case 0: { + // Nothing to do. + break; + } + case 1: { + sb.append(regs.get(0).regString()); + break; + } + default: { + RegisterSpec lastReg = regs.get(size - 1); + if (lastReg.getCategory() == 2) { + /* + * Add one to properly represent a list-final + * category-2 register. + */ + lastReg = lastReg.withOffset(1); + } + + sb.append(regs.get(0).regString()); + sb.append(".."); + sb.append(lastReg.regString()); + } + } + + sb.append("}, "); + sb.append(cstString(insn)); + + return sb.toString(); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + if (noteIndices) { + return cstComment(insn); + } else { + return ""; + } + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 4; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + if (!(insn instanceof CstInsn)) { + return false; + } + + CstInsn ci = (CstInsn) insn; + int cpi = ci.getIndex(); + + if (! unsignedFitsInShort(cpi)) { + return false; + } + + Constant cst = ci.getConstant(); + if (!((cst instanceof CstMethodRef) || + (cst instanceof CstType))) { + return false; + } + + RegisterSpecList regs = ci.getRegisters(); + int sz = regs.size(); + + if (sz == 0) { + return true; + } + + int first = regs.get(0).getReg(); + int next = first; + + if (!unsignedFitsInShort(first)) { + return false; + } + + for (int i = 0; i < sz; i++) { + RegisterSpec one = regs.get(i); + if (one.getReg() != next) { + return false; + } + next += one.getCategory(); + } + + return unsignedFitsInByte(next - first); + } + + /** {@inheritDoc} */ + @Override + public InsnFormat nextUp() { + return null; + } + + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int sz = regs.size(); + int cpi = ((CstInsn) insn).getIndex(); + int firstReg; + int count; + + if (sz == 0) { + firstReg = 0; + count = 0; + } else { + int lastReg = regs.get(sz - 1).getNextReg(); + firstReg = regs.get(0).getReg(); + count = lastReg - firstReg; + } + + write(out, + opcodeUnit(insn, count), + (short) cpi, + (short) firstReg); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dexgen/src/com/android/dexgen/util/FileUtils.java android-platform-dalvik-8.1.0+r23/dexgen/src/com/android/dexgen/util/FileUtils.java --- android-platform-dalvik-7.0.0+r33/dexgen/src/com/android/dexgen/util/FileUtils.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dexgen/src/com/android/dexgen/util/FileUtils.java 2018-10-24 12:07:51.000000000 +0000 @@ -72,17 +72,21 @@ byte[] result = new byte[length]; try { + // convert to try-with-resources once dexgen uses an Android API 19+ which supports it FileInputStream in = new FileInputStream(file); - int at = 0; - while (length > 0) { - int amt = in.read(result, at, length); - if (amt == -1) { - throw new RuntimeException(file + ": unexpected EOF"); + try { + int at = 0; + while (length > 0) { + int amt = in.read(result, at, length); + if (amt == -1) { + throw new RuntimeException(file + ": unexpected EOF"); + } + at += amt; + length -= amt; } - at += amt; - length -= amt; + } finally { + in.close(); } - in.close(); } catch (IOException ex) { throw new RuntimeException(file + ": trouble reading", ex); } diff -Nru android-platform-dalvik-7.0.0+r33/dx/Android.mk android-platform-dalvik-8.1.0+r23/dx/Android.mk --- android-platform-dalvik-7.0.0+r33/dx/Android.mk 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/Android.mk 2018-10-24 12:07:51.000000000 +0000 @@ -46,6 +46,25 @@ installed_mainDexClasses.rules := $(LOCAL_INSTALLED_MODULE) +# the mainDexClassesNoAapt rules +# ============================================================ +include $(CLEAR_VARS) +LOCAL_IS_HOST_MODULE := true +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := mainDexClassesNoAapt.rules + +include $(BUILD_SYSTEM)/base_rules.mk + +$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/dx$(COMMON_JAVA_PACKAGE_SUFFIX) +$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/mainDexClassesNoAapt.rules | $(ACP) + @echo "Copy: $(PRIVATE_MODULE) ($@)" + $(copy-file-to-new-target) + +INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE) + +installed_mainDexClassesNoAapt.rules := $(LOCAL_INSTALLED_MODULE) + # the shrinkedAndroid jar is a library used by the mainDexClasses script # ============================================================ include $(CLEAR_VARS) @@ -85,7 +104,8 @@ $(copy-file-to-new-target) $(hide) chmod 755 $@ -$(LOCAL_INSTALLED_MODULE): | $(installed_shrinkedAndroid) $(installed_mainDexClasses.rules) +$(LOCAL_INSTALLED_MODULE): | $(installed_shrinkedAndroid) $(installed_mainDexClasses.rules) \ + $(installed_mainDexClassesNoAapt.rules) INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE) endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK diff -Nru android-platform-dalvik-7.0.0+r33/dx/etc/mainDexClasses android-platform-dalvik-8.1.0+r23/dx/etc/mainDexClasses --- android-platform-dalvik-7.0.0+r33/dx/etc/mainDexClasses 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/etc/mainDexClasses 2018-10-24 12:07:51.000000000 +0000 @@ -58,11 +58,17 @@ cd "${oldwd}" baserules="${progdir}"/mainDexClasses.rules -if [ ! -r ${baserules} ]; then +if [ ! -r "${baserules}" ]; then echo `basename "$prog"`": can't find mainDexClasses.rules" 1>&2 exit 1 fi +extrarules="${progdir}"/mainDexClassesNoAapt.rules +if [ ! -r ${extrarules} ]; then + echo `basename "$prog"`": can't find mainDexClassesNoAapt.rules" 1>&2 + exit 1 +fi + jarfile=dx.jar libdir="$progdir" @@ -150,6 +156,9 @@ elif expr "x$1" : 'x--disable-annotation-resolution-workaround' >/dev/null; then disableKeepAnnotated=$1 shift 1 +elif expr "x$1" : "x--aapt-rules" >/dev/null; then + extrarules=$2 + shift 2 else break fi @@ -164,8 +173,8 @@ trap cleanTmp 0 -${proguard} -injars ${@} -dontwarn -forceprocessing -outjars ${tmpOut} \ +"${proguard}" -injars ${@} -dontwarn -forceprocessing -outjars "${tmpOut}" \ -libraryjars "${shrinkedAndroidJar}" -dontoptimize -dontobfuscate -dontpreverify \ - -include "${baserules}" 1>/dev/null || exit 10 + -include "${baserules}" -include "${extrarules}" 1>/dev/null || exit 10 java -cp "$jarpath" com.android.multidex.MainDexListBuilder ${disableKeepAnnotated} "${tmpOut}" ${@} || exit 11 diff -Nru android-platform-dalvik-7.0.0+r33/dx/etc/mainDexClasses.bat android-platform-dalvik-8.1.0+r23/dx/etc/mainDexClasses.bat --- android-platform-dalvik-7.0.0+r33/dx/etc/mainDexClasses.bat 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/etc/mainDexClasses.bat 2018-10-24 12:07:51.000000000 +0000 @@ -23,6 +23,7 @@ if not defined java_exe goto :EOF set baserules="%~dp0\mainDexClasses.rules" +set extrarules="%~dp0\mainDexClassesNoAapt.rules" REM Locate dx.jar in the directory where dx.bat was found. set jarfile=dx.jar @@ -86,6 +87,14 @@ goto firstArg :notDisable + + if %1 NEQ --aapt-rules goto notAapt + set "extrarules=%2" + shift + shift + goto firstArg + +:notAapt if defined params goto usage set params=%1 shift @@ -101,13 +110,13 @@ set "exitStatus=0" -call "%proguard%" -injars %params% -dontwarn -forceprocessing -outjars "%tmpJar%" -libraryjars "%shrinkedAndroidJar%" -dontoptimize -dontobfuscate -dontpreverify -include "%baserules%" 1>nul +call "%proguard%" -injars %params% -dontwarn -forceprocessing -outjars "%tmpJar%" -libraryjars "%shrinkedAndroidJar%" -dontoptimize -dontobfuscate -dontpreverify -include "%baserules%" -include "%extrarules%" 1>nul if DEFINED output goto redirect -call "%java_exe%" -Djava.ext.dirs="%frameworkdir%" com.android.multidex.MainDexListBuilder "%disableKeepAnnotated%" "%tmpJar%" "%params%" +call "%java_exe%" -Djava.ext.dirs="%frameworkdir%" com.android.multidex.MainDexListBuilder %disableKeepAnnotated% "%tmpJar%" "%params%" goto afterClassReferenceListBuilder :redirect -call "%java_exe%" -Djava.ext.dirs="%frameworkdir%" com.android.multidex.MainDexListBuilder "%disableKeepAnnotated%" "%tmpJar%" "%params%" 1>"%output%" +call "%java_exe%" -Djava.ext.dirs="%frameworkdir%" com.android.multidex.MainDexListBuilder %disableKeepAnnotated% "%tmpJar%" "%params%" 1>"%output%" :afterClassReferenceListBuilder del %tmpJar% diff -Nru android-platform-dalvik-7.0.0+r33/dx/etc/mainDexClassesNoAapt.rules android-platform-dalvik-8.1.0+r23/dx/etc/mainDexClassesNoAapt.rules --- android-platform-dalvik-7.0.0+r33/dx/etc/mainDexClassesNoAapt.rules 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/etc/mainDexClassesNoAapt.rules 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,13 @@ + -keep public class * extends android.app.Activity { + (); + } + -keep public class * extends android.app.Service { + (); + } + -keep public class * extends android.content.ContentProvider { + (); + } + -keep public class * extends android.content.BroadcastReceiver { + (); + } + diff -Nru android-platform-dalvik-7.0.0+r33/dx/etc/mainDexClasses.rules android-platform-dalvik-8.1.0+r23/dx/etc/mainDexClasses.rules --- android-platform-dalvik-7.0.0+r33/dx/etc/mainDexClasses.rules 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/etc/mainDexClasses.rules 2018-10-24 12:07:51.000000000 +0000 @@ -5,18 +5,6 @@ (); void attachBaseContext(android.content.Context); } - -keep public class * extends android.app.Activity { - (); - } - -keep public class * extends android.app.Service { - (); - } - -keep public class * extends android.content.ContentProvider { - (); - } - -keep public class * extends android.content.BroadcastReceiver { - (); - } -keep public class * extends android.app.backup.BackupAgent { (); } @@ -25,3 +13,8 @@ -keep public class * extends java.lang.annotation.Annotation { *; } +# Keep old fashion tests in the main dex or they'll be silently ignored by InstrumentationTestRunner + -keep public class * extends android.test.InstrumentationTestCase { + (); + } + diff -Nru android-platform-dalvik-7.0.0+r33/dx/junit-tests/Android.mk android-platform-dalvik-8.1.0+r23/dx/junit-tests/Android.mk --- android-platform-dalvik-7.0.0+r33/dx/junit-tests/Android.mk 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/junit-tests/Android.mk 2018-10-24 12:07:51.000000000 +0000 @@ -3,7 +3,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_JAVA_LIBRARIES := dx junit +LOCAL_JAVA_LIBRARIES := dx junit-host LOCAL_MODULE_TAGS := tests LOCAL_MODULE:= dx-tests include $(BUILD_HOST_JAVA_LIBRARY) diff -Nru android-platform-dalvik-7.0.0+r33/dx/junit-tests/com/android/dex/EncodedValueReaderTest.java android-platform-dalvik-8.1.0+r23/dx/junit-tests/com/android/dex/EncodedValueReaderTest.java --- android-platform-dalvik-7.0.0+r33/dx/junit-tests/com/android/dex/EncodedValueReaderTest.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/junit-tests/com/android/dex/EncodedValueReaderTest.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2011 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.dex; + +import com.android.dex.util.ByteArrayByteInput; +import junit.framework.TestCase; + +public final class EncodedValueReaderTest extends TestCase { + + public void testReadByte() { + assertEquals((byte) 0x80, readerOf(0, 0x80).readByte()); + assertEquals((byte) 0xff, readerOf(0, 0xff).readByte()); + assertEquals((byte) 0x00, readerOf(0, 0x00).readByte()); + assertEquals((byte) 0x01, readerOf(0, 0x01).readByte()); + assertEquals((byte) 0x7f, readerOf(0, 0x7f).readByte()); + } + + public void testReadShort() { + assertEquals((short) 0x8000, readerOf(34, 0x00, 0x80).readShort()); + assertEquals((short) 0, readerOf( 2, 0x00).readShort()); + assertEquals((short) 0xab, readerOf(34, 0xab, 0x00).readShort()); + assertEquals((short) 0xabcd, readerOf(34, 0xcd, 0xab).readShort()); + assertEquals((short) 0x7FFF, readerOf(34, 0xff, 0x7f).readShort()); + } + + public void testReadInt() { + assertEquals(0x80000000, readerOf(100, 0x00, 0x00, 0x00, 0x80).readInt()); + assertEquals( 0x00, readerOf( 4, 0x00).readInt()); + assertEquals( 0xab, readerOf( 36, 0xab, 0x00).readInt()); + assertEquals( 0xabcd, readerOf( 68, 0xcd, 0xab, 0x00).readInt()); + assertEquals( 0xabcdef, readerOf(100, 0xef, 0xcd, 0xab, 0x00).readInt()); + assertEquals(0xabcdef01, readerOf(100, 0x01, 0xef, 0xcd, 0xab).readInt()); + assertEquals(0x7fffffff, readerOf(100, 0xff, 0xff, 0xff, 127).readInt()); + } + + public void testReadLong() { + assertEquals(0x8000000000000000L, readerOf( -26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80).readLong()); + assertEquals( 0x00L, readerOf( 6, 0x00).readLong()); + assertEquals( 0xabL, readerOf( 38, 0xab, 0x00).readLong()); + assertEquals( 0xabcdL, readerOf( 70, 0xcd, 0xab, 0x00).readLong()); + assertEquals( 0xabcdefL, readerOf( 102, 0xef, 0xcd, 0xab, 0x00).readLong()); + assertEquals( 0xabcdef01L, readerOf(-122, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong()); + assertEquals( 0xabcdef0123L, readerOf( -90, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong()); + assertEquals( 0xabcdef012345L, readerOf( -58, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong()); + assertEquals( 0xabcdef01234567L, readerOf( -26, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong()); + assertEquals(0xabcdef0123456789L, readerOf( -26, 0x89, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab).readLong()); + assertEquals(0x7fffffffffffffffL, readerOf( -26, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f).readLong()); + } + + public void testReadFloat() { + assertEquals(Float.NEGATIVE_INFINITY, readerOf(48, -128, -1).readFloat()); + assertEquals(Float.POSITIVE_INFINITY, readerOf(48, -128, 127).readFloat()); + assertEquals(Float.NaN, readerOf(48, -64, 127).readFloat()); + assertEquals(-0.0f, readerOf(16, -128).readFloat()); + assertEquals(0.0f, readerOf(16, 0).readFloat()); + assertEquals(0.5f, readerOf(16, 63).readFloat()); + assertEquals(1f, readerOf(48, -128, 63).readFloat()); + assertEquals(1.0E06f, readerOf(80, 36, 116, 73).readFloat()); + assertEquals(1.0E12f, readerOf(112, -91, -44, 104, 83).readFloat()); + } + + public void testReadDouble() { + assertEquals(Double.NEGATIVE_INFINITY, readerOf(49, -16, -1).readDouble()); + assertEquals(Double.POSITIVE_INFINITY, readerOf(49, -16, 127).readDouble()); + assertEquals(Double.NaN, readerOf(49, -8, 127).readDouble()); + assertEquals(-0.0, readerOf(17, -128).readDouble()); + assertEquals(0.0, readerOf(17, 0).readDouble()); + assertEquals(0.5, readerOf(49, -32, 63).readDouble()); + assertEquals(1.0, readerOf(49, -16, 63).readDouble()); + assertEquals(1.0E06, readerOf(113, -128, -124, 46, 65).readDouble()); + assertEquals(1.0E12, readerOf(-111, -94, -108, 26, 109, 66).readDouble()); + assertEquals(1.0E24, readerOf(-15, -76, -99, -39, 121, 67, 120, -22, 68).readDouble()); + } + + public void testReadChar() { + assertEquals('\u0000', readerOf( 3, 0x00).readChar()); + assertEquals('\u00ab', readerOf( 3, 0xab).readChar()); + assertEquals('\uabcd', readerOf(35, 0xcd, 0xab).readChar()); + assertEquals('\uffff', readerOf(35, 0xff, 0xff).readChar()); + } + + public void testReadBoolean() { + assertEquals(true, readerOf(63).readBoolean()); + assertEquals(false, readerOf(31).readBoolean()); + } + + public void testReadNull() { + readerOf(30).readNull(); + } + + public void testReadReference() { + assertEquals( 0xab, readerOf(0x17, 0xab).readString()); + assertEquals( 0xabcd, readerOf(0x37, 0xcd, 0xab).readString()); + assertEquals( 0xabcdef, readerOf(0x57, 0xef, 0xcd, 0xab).readString()); + assertEquals(0xabcdef01, readerOf(0x77, 0x01, 0xef, 0xcd, 0xab).readString()); + } + + public void testReadWrongType() { + try { + readerOf(0x17, 0xab).readField(); + fail(); + } catch (IllegalStateException expected) { + } + } + + private EncodedValueReader readerOf(int... bytes) { + byte[] data = new byte[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + data[i] = (byte) bytes[i]; + } + return new EncodedValueReader(new ByteArrayByteInput(data)); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/junit-tests/com/android/dx/merge/DexMergerTest.java android-platform-dalvik-8.1.0+r23/dx/junit-tests/com/android/dx/merge/DexMergerTest.java --- android-platform-dalvik-7.0.0+r33/dx/junit-tests/com/android/dx/merge/DexMergerTest.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/junit-tests/com/android/dx/merge/DexMergerTest.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2017 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.dx.merge; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import com.android.dex.Dex; +import com.android.dx.command.Main; +import com.android.dx.command.dexer.DxContext; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class DexMergerTest { + static class NoFieldsClassA { + } + static class NoFieldsClassB { + } + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void test_merge_dexesWithEmptyFieldsSection() throws IOException { + List outputDexes = new ArrayList<>(); + outputDexes.add(getDexForClass(NoFieldsClassA.class)); + outputDexes.add(getDexForClass(NoFieldsClassB.class)); + + Dex merged = + new DexMerger( + outputDexes.toArray(new Dex[outputDexes.size()]), + CollisionPolicy.FAIL, + new DxContext()) + .merge(); + assertNotNull(merged); + assertNotNull(merged.getTableOfContents()); + assertEquals(0, merged.getTableOfContents().fieldIds.off); + } + + private Dex getDexForClass(Class clazz) throws IOException { + String path = clazz.getName().replace('.', '/') + ".class"; + Path classesJar = temporaryFolder.newFile(clazz.getName() + ".jar").toPath(); + try (InputStream in = getClass().getClassLoader().getResourceAsStream(path); + ZipOutputStream zip = new ZipOutputStream(Files.newOutputStream(classesJar))) { + + ZipEntry entry = new ZipEntry(path); + zip.putNextEntry(entry); + zip.write(readEntireStream(in)); + zip.closeEntry(); + } + + Path output = temporaryFolder.newFolder().toPath(); + Main.main(new String[]{"--dex", "--output=" + output.toString(), classesJar.toString()}); + + return new Dex(Files.readAllBytes(output.resolve("classes.dex"))); + } + + private static byte[] readEntireStream(InputStream inputStream) throws IOException { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + + int count; + while ((count = inputStream.read(buffer)) != -1) { + bytesOut.write(buffer, 0, count); + } + + return bytesOut.toByteArray(); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/junit-tests/com/android/dx/rop/cst/CstTypeTest.java android-platform-dalvik-8.1.0+r23/dx/junit-tests/com/android/dx/rop/cst/CstTypeTest.java --- android-platform-dalvik-7.0.0+r33/dx/junit-tests/com/android/dx/rop/cst/CstTypeTest.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/junit-tests/com/android/dx/rop/cst/CstTypeTest.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 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.dx.rop.cst; + +import com.android.dx.rop.type.Type; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class CstTypeTest { + + + @Test + public void checkClearInternTable() { + CstType boolArray = CstType.BOOLEAN_ARRAY; + assertTrue(boolArray == CstType.intern(Type.BOOLEAN_ARRAY)); + CstType myClass = CstType.intern(Type.intern("Lcom/example/Foo;")); + + CstType.clearInternTable(); + Type.clearInternTable(); + + assertTrue(boolArray == CstType.intern(Type.BOOLEAN_ARRAY)); + CstType myClass2 = CstType.intern(Type.intern("Lcom/example/Foo;")); + assertEquals(myClass.getClassType(), myClass2.getClassType()); + assertFalse(myClass == myClass2); + } +} \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/dx/junit-tests/com/android/dx/rop/type/TypeTest.java android-platform-dalvik-8.1.0+r23/dx/junit-tests/com/android/dx/rop/type/TypeTest.java --- android-platform-dalvik-7.0.0+r33/dx/junit-tests/com/android/dx/rop/type/TypeTest.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/junit-tests/com/android/dx/rop/type/TypeTest.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 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.dx.rop.type; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TypeTest { + + @Test + public void testClearInternTable() { + Type boolArray = Type.BOOLEAN.getArrayType(); + assertTrue(boolArray == Type.BOOLEAN_ARRAY); + assertTrue(boolArray == Type.intern("[Z")); + Type myClass = Type.intern("Lcom/example/Foo;"); + + Type.clearInternTable(); + + assertTrue(boolArray == Type.intern("[Z")); + Type myClass2 = Type.intern("Lcom/example/Foo;"); + assertEquals(myClass, myClass2); + assertFalse(myClass == myClass2); + } +} \ No newline at end of file diff -Nru android-platform-dalvik-7.0.0+r33/dx/shrinkedAndroid.proguard.flags android-platform-dalvik-8.1.0+r23/dx/shrinkedAndroid.proguard.flags --- android-platform-dalvik-7.0.0+r33/dx/shrinkedAndroid.proguard.flags 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/shrinkedAndroid.proguard.flags 2018-10-24 12:07:51.000000000 +0000 @@ -19,4 +19,6 @@ } -keep public class java.lang.annotation.Annotation { } +-keep public class * extends android.test.InstrumentationTestCase { +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/Android.mk android-platform-dalvik-8.1.0+r23/dx/src/Android.mk --- android-platform-dalvik-7.0.0+r33/dx/src/Android.mk 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/Android.mk 2018-10-24 12:07:51.000000000 +0000 @@ -5,9 +5,7 @@ # This tool is prebuilt if we're doing an app-only build. ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),) -dx_src_files := \ - $(call all-subdir-java-files) \ - $(call all-java-files-under,../../../libcore/dex/src/main/java) +dx_src_files := $(call all-subdir-java-files) # dx java library # ============================================================ @@ -17,10 +15,7 @@ LOCAL_JAR_MANIFEST := ../etc/manifest.txt LOCAL_MODULE_TAGS := optional LOCAL_MODULE:= dx - -# Force java 6 target because we want java 6 runtimes to run dx, at least as long as the android SDK -# requirement JDK 6. -LOCAL_JAVACFLAGS:= -source 6 -target 6 +LOCAL_JAVA_LANGUAGE_VERSION := 1.7 include $(BUILD_HOST_JAVA_LIBRARY) diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/Annotation.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/Annotation.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/Annotation.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/Annotation.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011 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.dex; + +import static com.android.dex.EncodedValueReader.ENCODED_ANNOTATION; + +/** + * An annotation. + */ +public final class Annotation implements Comparable { + private final Dex dex; + private final byte visibility; + private final EncodedValue encodedAnnotation; + + public Annotation(Dex dex, byte visibility, EncodedValue encodedAnnotation) { + this.dex = dex; + this.visibility = visibility; + this.encodedAnnotation = encodedAnnotation; + } + + public byte getVisibility() { + return visibility; + } + + public EncodedValueReader getReader() { + return new EncodedValueReader(encodedAnnotation, ENCODED_ANNOTATION); + } + + public int getTypeIndex() { + EncodedValueReader reader = getReader(); + reader.readAnnotation(); + return reader.getAnnotationType(); + } + + public void writeTo(Dex.Section out) { + out.writeByte(visibility); + encodedAnnotation.writeTo(out); + } + + @Override public int compareTo(Annotation other) { + return encodedAnnotation.compareTo(other.encodedAnnotation); + } + + @Override public String toString() { + return dex == null + ? visibility + " " + getTypeIndex() + : visibility + " " + dex.typeNames().get(getTypeIndex()); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/CallSiteId.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/CallSiteId.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/CallSiteId.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/CallSiteId.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 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.dex; + +import com.android.dex.Dex.Section; +import com.android.dex.util.Unsigned; + +/** + * A call_site_id_item: https://source.android.com/devices/tech/dalvik/dex-format#call-site-id-item + */ +public class CallSiteId implements Comparable { + + private final Dex dex; + private final int offset; + + public CallSiteId(Dex dex, int offset) { + this.dex = dex; + this.offset = offset; + } + + @Override + public int compareTo(CallSiteId o) { + return Unsigned.compare(offset, o.offset); + } + + public int getCallSiteOffset() { + return offset; + } + + public void writeTo(Section out) { + out.writeInt(offset); + } + + @Override + public String toString() { + if (dex == null) { + return String.valueOf(offset); + } + return dex.protoIds().get(offset).toString(); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/ClassData.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/ClassData.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/ClassData.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/ClassData.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2011 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.dex; + +public final class ClassData { + private final Field[] staticFields; + private final Field[] instanceFields; + private final Method[] directMethods; + private final Method[] virtualMethods; + + public ClassData(Field[] staticFields, Field[] instanceFields, + Method[] directMethods, Method[] virtualMethods) { + this.staticFields = staticFields; + this.instanceFields = instanceFields; + this.directMethods = directMethods; + this.virtualMethods = virtualMethods; + } + + public Field[] getStaticFields() { + return staticFields; + } + + public Field[] getInstanceFields() { + return instanceFields; + } + + public Method[] getDirectMethods() { + return directMethods; + } + + public Method[] getVirtualMethods() { + return virtualMethods; + } + + public Field[] allFields() { + Field[] result = new Field[staticFields.length + instanceFields.length]; + System.arraycopy(staticFields, 0, result, 0, staticFields.length); + System.arraycopy(instanceFields, 0, result, staticFields.length, instanceFields.length); + return result; + } + + public Method[] allMethods() { + Method[] result = new Method[directMethods.length + virtualMethods.length]; + System.arraycopy(directMethods, 0, result, 0, directMethods.length); + System.arraycopy(virtualMethods, 0, result, directMethods.length, virtualMethods.length); + return result; + } + + public static class Field { + private final int fieldIndex; + private final int accessFlags; + + public Field(int fieldIndex, int accessFlags) { + this.fieldIndex = fieldIndex; + this.accessFlags = accessFlags; + } + + public int getFieldIndex() { + return fieldIndex; + } + + public int getAccessFlags() { + return accessFlags; + } + } + + public static class Method { + private final int methodIndex; + private final int accessFlags; + private final int codeOffset; + + public Method(int methodIndex, int accessFlags, int codeOffset) { + this.methodIndex = methodIndex; + this.accessFlags = accessFlags; + this.codeOffset = codeOffset; + } + + public int getMethodIndex() { + return methodIndex; + } + + public int getAccessFlags() { + return accessFlags; + } + + public int getCodeOffset() { + return codeOffset; + } + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/ClassDef.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/ClassDef.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/ClassDef.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/ClassDef.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2011 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.dex; + +/** + * A type definition. + */ +public final class ClassDef { + public static final int NO_INDEX = -1; + private final Dex buffer; + private final int offset; + private final int typeIndex; + private final int accessFlags; + private final int supertypeIndex; + private final int interfacesOffset; + private final int sourceFileIndex; + private final int annotationsOffset; + private final int classDataOffset; + private final int staticValuesOffset; + + public ClassDef(Dex buffer, int offset, int typeIndex, int accessFlags, + int supertypeIndex, int interfacesOffset, int sourceFileIndex, + int annotationsOffset, int classDataOffset, int staticValuesOffset) { + this.buffer = buffer; + this.offset = offset; + this.typeIndex = typeIndex; + this.accessFlags = accessFlags; + this.supertypeIndex = supertypeIndex; + this.interfacesOffset = interfacesOffset; + this.sourceFileIndex = sourceFileIndex; + this.annotationsOffset = annotationsOffset; + this.classDataOffset = classDataOffset; + this.staticValuesOffset = staticValuesOffset; + } + + public int getOffset() { + return offset; + } + + public int getTypeIndex() { + return typeIndex; + } + + public int getSupertypeIndex() { + return supertypeIndex; + } + + public int getInterfacesOffset() { + return interfacesOffset; + } + + public short[] getInterfaces() { + return buffer.readTypeList(interfacesOffset).getTypes(); + } + + public int getAccessFlags() { + return accessFlags; + } + + public int getSourceFileIndex() { + return sourceFileIndex; + } + + public int getAnnotationsOffset() { + return annotationsOffset; + } + + public int getClassDataOffset() { + return classDataOffset; + } + + public int getStaticValuesOffset() { + return staticValuesOffset; + } + + @Override public String toString() { + if (buffer == null) { + return typeIndex + " " + supertypeIndex; + } + + StringBuilder result = new StringBuilder(); + result.append(buffer.typeNames().get(typeIndex)); + if (supertypeIndex != NO_INDEX) { + result.append(" extends ").append(buffer.typeNames().get(supertypeIndex)); + } + return result.toString(); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/Code.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/Code.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/Code.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/Code.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2011 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.dex; + +public final class Code { + private final int registersSize; + private final int insSize; + private final int outsSize; + private final int debugInfoOffset; + private final short[] instructions; + private final Try[] tries; + private final CatchHandler[] catchHandlers; + + public Code(int registersSize, int insSize, int outsSize, int debugInfoOffset, + short[] instructions, Try[] tries, CatchHandler[] catchHandlers) { + this.registersSize = registersSize; + this.insSize = insSize; + this.outsSize = outsSize; + this.debugInfoOffset = debugInfoOffset; + this.instructions = instructions; + this.tries = tries; + this.catchHandlers = catchHandlers; + } + + public int getRegistersSize() { + return registersSize; + } + + public int getInsSize() { + return insSize; + } + + public int getOutsSize() { + return outsSize; + } + + public int getDebugInfoOffset() { + return debugInfoOffset; + } + + public short[] getInstructions() { + return instructions; + } + + public Try[] getTries() { + return tries; + } + + public CatchHandler[] getCatchHandlers() { + return catchHandlers; + } + + public static class Try { + final int startAddress; + final int instructionCount; + final int catchHandlerIndex; + + Try(int startAddress, int instructionCount, int catchHandlerIndex) { + this.startAddress = startAddress; + this.instructionCount = instructionCount; + this.catchHandlerIndex = catchHandlerIndex; + } + + public int getStartAddress() { + return startAddress; + } + + public int getInstructionCount() { + return instructionCount; + } + + /** + * Returns this try's catch handler index. Note that + * this is distinct from the its catch handler offset. + */ + public int getCatchHandlerIndex() { + return catchHandlerIndex; + } + } + + public static class CatchHandler { + final int[] typeIndexes; + final int[] addresses; + final int catchAllAddress; + final int offset; + + public CatchHandler(int[] typeIndexes, int[] addresses, int catchAllAddress, int offset) { + this.typeIndexes = typeIndexes; + this.addresses = addresses; + this.catchAllAddress = catchAllAddress; + this.offset = offset; + } + + public int[] getTypeIndexes() { + return typeIndexes; + } + + public int[] getAddresses() { + return addresses; + } + + public int getCatchAllAddress() { + return catchAllAddress; + } + + public int getOffset() { + return offset; + } + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/DexException.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/DexException.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/DexException.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/DexException.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2011 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.dex; + +import com.android.dex.util.ExceptionWithContext; + +/** + * Thrown when there's a format problem reading, writing, or generally + * processing a dex file. + */ +public class DexException extends ExceptionWithContext { + public DexException(String message) { + super(message); + } + + public DexException(Throwable cause) { + super(cause); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/DexFormat.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/DexFormat.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/DexFormat.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/DexFormat.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2011 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.dex; + +/** + * Constants that show up in and are otherwise related to {@code .dex} + * files, and helper methods for same. + */ +public final class DexFormat { + private DexFormat() {} + + /** API level to target in order to generate invoke-polymorphic */ + public static final int API_INVOKE_POLYMORPHIC = 26; + + /** API level to target in order to pass through default and static interface methods */ + public static final int API_DEFAULT_INTERFACE_METHODS = 24; + + /** API level to target in order to suppress extended opcode usage */ + public static final int API_NO_EXTENDED_OPCODES = 13; + + /** + * API level to target in order to produce the most modern file + * format + */ + public static final int API_CURRENT = API_INVOKE_POLYMORPHIC; + + /** dex file version number for API level 26 and earlier */ + public static final String VERSION_FOR_API_26 = "038"; + + /** dex file version number for API level 24 and earlier */ + public static final String VERSION_FOR_API_24 = "037"; + + /** dex file version number for API level 13 and earlier */ + public static final String VERSION_FOR_API_13 = "035"; + + /** + * Dex file version number for dalvik. + *

+ * Note: Dex version 36 was loadable in some versions of Dalvik but was never fully supported or + * completed and is not considered a valid dex file format. + *

+ */ + public static final String VERSION_CURRENT = VERSION_FOR_API_26; + + /** + * file name of the primary {@code .dex} file inside an + * application or library {@code .jar} file + */ + public static final String DEX_IN_JAR_NAME = "classes.dex"; + + /** common prefix for all dex file "magic numbers" */ + public static final String MAGIC_PREFIX = "dex\n"; + + /** common suffix for all dex file "magic numbers" */ + public static final String MAGIC_SUFFIX = "\0"; + + /** + * value used to indicate endianness of file contents + */ + public static final int ENDIAN_TAG = 0x12345678; + + /** + * Maximum addressable field or method index. + * The largest addressable member is 0xffff, in the "instruction formats" spec as field@CCCC or + * meth@CCCC. + */ + public static final int MAX_MEMBER_IDX = 0xFFFF; + + /** + * Maximum addressable type index. + * The largest addressable type is 0xffff, in the "instruction formats" spec as type@CCCC. + */ + public static final int MAX_TYPE_IDX = 0xFFFF; + + /** + * Returns the API level corresponding to the given magic number, + * or {@code -1} if the given array is not a well-formed dex file + * magic number. + */ + public static int magicToApi(byte[] magic) { + if (magic.length != 8) { + return -1; + } + + if ((magic[0] != 'd') || (magic[1] != 'e') || (magic[2] != 'x') || (magic[3] != '\n') || + (magic[7] != '\0')) { + return -1; + } + + String version = "" + ((char) magic[4]) + ((char) magic[5]) +((char) magic[6]); + + if (version.equals(VERSION_FOR_API_13)) { + return API_NO_EXTENDED_OPCODES; + } else if (version.equals(VERSION_FOR_API_24)) { + return API_DEFAULT_INTERFACE_METHODS; + } else if (version.equals(VERSION_FOR_API_26)) { + return API_INVOKE_POLYMORPHIC; + } else if (version.equals(VERSION_CURRENT)) { + return API_CURRENT; + } + + return -1; + } + + /** + * Returns the magic number corresponding to the given target API level. + */ + public static String apiToMagic(int targetApiLevel) { + String version; + + if (targetApiLevel >= API_CURRENT) { + version = VERSION_CURRENT; + } else if (targetApiLevel >= API_INVOKE_POLYMORPHIC) { + version = VERSION_FOR_API_26; + } else if (targetApiLevel >= API_DEFAULT_INTERFACE_METHODS) { + version = VERSION_FOR_API_24; + } else { + version = VERSION_FOR_API_13; + } + + return MAGIC_PREFIX + version + MAGIC_SUFFIX; + } + + public static boolean isSupportedDexMagic(byte[] magic) { + int api = magicToApi(magic); + return api > 0; + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/DexIndexOverflowException.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/DexIndexOverflowException.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/DexIndexOverflowException.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/DexIndexOverflowException.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,30 @@ +/* + * 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 com.android.dex; + +/** + * Thrown when there's an index overflow writing a dex file. + */ +public final class DexIndexOverflowException extends DexException { + public DexIndexOverflowException(String message) { + super(message); + } + + public DexIndexOverflowException(Throwable cause) { + super(cause); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/Dex.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/Dex.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/Dex.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/Dex.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,808 @@ +/* + * Copyright (C) 2011 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.dex; + +import com.android.dex.Code.CatchHandler; +import com.android.dex.Code.Try; +import com.android.dex.MethodHandle.MethodHandleType; +import com.android.dex.util.ByteInput; +import com.android.dex.util.ByteOutput; +import com.android.dex.util.FileUtils; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UTFDataFormatException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.AbstractList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.RandomAccess; +import java.util.zip.Adler32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * The bytes of a dex file in memory for reading and writing. All int offsets + * are unsigned. + */ +public final class Dex { + private static final int CHECKSUM_OFFSET = 8; + private static final int CHECKSUM_SIZE = 4; + private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE; + private static final int SIGNATURE_SIZE = 20; + // Provided as a convenience to avoid a memory allocation to benefit Dalvik. + // Note: libcore.util.EmptyArray cannot be accessed when this code isn't run on Dalvik. + static final short[] EMPTY_SHORT_ARRAY = new short[0]; + + private ByteBuffer data; + private final TableOfContents tableOfContents = new TableOfContents(); + private int nextSectionStart = 0; + private final StringTable strings = new StringTable(); + private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable(); + private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable(); + private final ProtoIdTable protoIds = new ProtoIdTable(); + private final FieldIdTable fieldIds = new FieldIdTable(); + private final MethodIdTable methodIds = new MethodIdTable(); + + /** + * Creates a new dex that reads from {@code data}. It is an error to modify + * {@code data} after using it to create a dex buffer. + */ + public Dex(byte[] data) throws IOException { + this(ByteBuffer.wrap(data)); + } + + private Dex(ByteBuffer data) throws IOException { + this.data = data; + this.data.order(ByteOrder.LITTLE_ENDIAN); + this.tableOfContents.readFrom(this); + } + + /** + * Creates a new empty dex of the specified size. + */ + public Dex(int byteCount) throws IOException { + this.data = ByteBuffer.wrap(new byte[byteCount]); + this.data.order(ByteOrder.LITTLE_ENDIAN); + } + + /** + * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}. + */ + public Dex(InputStream in) throws IOException { + try { + loadFrom(in); + } finally { + in.close(); + } + } + + /** + * Creates a new dex buffer from the dex file {@code file}. + */ + public Dex(File file) throws IOException { + if (FileUtils.hasArchiveSuffix(file.getName())) { + ZipFile zipFile = new ZipFile(file); + ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME); + if (entry != null) { + try (InputStream inputStream = zipFile.getInputStream(entry)) { + loadFrom(inputStream); + } + zipFile.close(); + } else { + throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file); + } + } else if (file.getName().endsWith(".dex")) { + try (InputStream inputStream = new FileInputStream(file)) { + loadFrom(inputStream); + } + } else { + throw new DexException("unknown output extension: " + file); + } + } + + /** + * It is the caller's responsibility to close {@code in}. + */ + private void loadFrom(InputStream in) throws IOException { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + + int count; + while ((count = in.read(buffer)) != -1) { + bytesOut.write(buffer, 0, count); + } + + this.data = ByteBuffer.wrap(bytesOut.toByteArray()); + this.data.order(ByteOrder.LITTLE_ENDIAN); + this.tableOfContents.readFrom(this); + } + + private static void checkBounds(int index, int length) { + if (index < 0 || index >= length) { + throw new IndexOutOfBoundsException("index:" + index + ", length=" + length); + } + } + + public void writeTo(OutputStream out) throws IOException { + byte[] buffer = new byte[8192]; + ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe + data.clear(); + while (data.hasRemaining()) { + int count = Math.min(buffer.length, data.remaining()); + data.get(buffer, 0, count); + out.write(buffer, 0, count); + } + } + + public void writeTo(File dexOut) throws IOException { + try (OutputStream out = new FileOutputStream(dexOut)) { + writeTo(out); + } + } + + public TableOfContents getTableOfContents() { + return tableOfContents; + } + + public Section open(int position) { + if (position < 0 || position >= data.capacity()) { + throw new IllegalArgumentException("position=" + position + + " length=" + data.capacity()); + } + ByteBuffer sectionData = data.duplicate(); + sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary? + sectionData.position(position); + sectionData.limit(data.capacity()); + return new Section("section", sectionData); + } + + public Section appendSection(int maxByteCount, String name) { + if ((maxByteCount & 3) != 0) { + throw new IllegalStateException("Not four byte aligned!"); + } + int limit = nextSectionStart + maxByteCount; + ByteBuffer sectionData = data.duplicate(); + sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary? + sectionData.position(nextSectionStart); + sectionData.limit(limit); + Section result = new Section(name, sectionData); + nextSectionStart = limit; + return result; + } + + public int getLength() { + return data.capacity(); + } + + public int getNextSectionStart() { + return nextSectionStart; + } + + /** + * Returns a copy of the the bytes of this dex. + */ + public byte[] getBytes() { + ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe + byte[] result = new byte[data.capacity()]; + data.position(0); + data.get(result); + return result; + } + + public List strings() { + return strings; + } + + public List typeIds() { + return typeIds; + } + + public List typeNames() { + return typeNames; + } + + public List protoIds() { + return protoIds; + } + + public List fieldIds() { + return fieldIds; + } + + public List methodIds() { + return methodIds; + } + + public Iterable classDefs() { + return new ClassDefIterable(); + } + + public TypeList readTypeList(int offset) { + if (offset == 0) { + return TypeList.EMPTY; + } + return open(offset).readTypeList(); + } + + public ClassData readClassData(ClassDef classDef) { + int offset = classDef.getClassDataOffset(); + if (offset == 0) { + throw new IllegalArgumentException("offset == 0"); + } + return open(offset).readClassData(); + } + + public Code readCode(ClassData.Method method) { + int offset = method.getCodeOffset(); + if (offset == 0) { + throw new IllegalArgumentException("offset == 0"); + } + return open(offset).readCode(); + } + + /** + * Returns the signature of all but the first 32 bytes of this dex. The + * first 32 bytes of dex files are not specified to be included in the + * signature. + */ + public byte[] computeSignature() throws IOException { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(); + } + byte[] buffer = new byte[8192]; + ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe + data.limit(data.capacity()); + data.position(SIGNATURE_OFFSET + SIGNATURE_SIZE); + while (data.hasRemaining()) { + int count = Math.min(buffer.length, data.remaining()); + data.get(buffer, 0, count); + digest.update(buffer, 0, count); + } + return digest.digest(); + } + + /** + * Returns the checksum of all but the first 12 bytes of {@code dex}. + */ + public int computeChecksum() throws IOException { + Adler32 adler32 = new Adler32(); + byte[] buffer = new byte[8192]; + ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe + data.limit(data.capacity()); + data.position(CHECKSUM_OFFSET + CHECKSUM_SIZE); + while (data.hasRemaining()) { + int count = Math.min(buffer.length, data.remaining()); + data.get(buffer, 0, count); + adler32.update(buffer, 0, count); + } + return (int) adler32.getValue(); + } + + /** + * Generates the signature and checksum of the dex file {@code out} and + * writes them to the file. + */ + public void writeHashes() throws IOException { + open(SIGNATURE_OFFSET).write(computeSignature()); + open(CHECKSUM_OFFSET).writeInt(computeChecksum()); + } + + /** + * Look up a descriptor index from a type index. Cheaper than: + * {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();} + */ + public int descriptorIndexFromTypeIndex(int typeIndex) { + checkBounds(typeIndex, tableOfContents.typeIds.size); + int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex); + return data.getInt(position); + } + + + public final class Section implements ByteInput, ByteOutput { + private final String name; + private final ByteBuffer data; + private final int initialPosition; + + private Section(String name, ByteBuffer data) { + this.name = name; + this.data = data; + this.initialPosition = data.position(); + } + + public int getPosition() { + return data.position(); + } + + public int readInt() { + return data.getInt(); + } + + public short readShort() { + return data.getShort(); + } + + public int readUnsignedShort() { + return readShort() & 0xffff; + } + + @Override + public byte readByte() { + return data.get(); + } + + public byte[] readByteArray(int length) { + byte[] result = new byte[length]; + data.get(result); + return result; + } + + public short[] readShortArray(int length) { + if (length == 0) { + return EMPTY_SHORT_ARRAY; + } + short[] result = new short[length]; + for (int i = 0; i < length; i++) { + result[i] = readShort(); + } + return result; + } + + public int readUleb128() { + return Leb128.readUnsignedLeb128(this); + } + + public int readUleb128p1() { + return Leb128.readUnsignedLeb128(this) - 1; + } + + public int readSleb128() { + return Leb128.readSignedLeb128(this); + } + + public void writeUleb128p1(int i) { + writeUleb128(i + 1); + } + + public TypeList readTypeList() { + int size = readInt(); + short[] types = readShortArray(size); + alignToFourBytes(); + return new TypeList(Dex.this, types); + } + + public String readString() { + int offset = readInt(); + int savedPosition = data.position(); + int savedLimit = data.limit(); + data.position(offset); + data.limit(data.capacity()); + try { + int expectedLength = readUleb128(); + String result = Mutf8.decode(this, new char[expectedLength]); + if (result.length() != expectedLength) { + throw new DexException("Declared length " + expectedLength + + " doesn't match decoded length of " + result.length()); + } + return result; + } catch (UTFDataFormatException e) { + throw new DexException(e); + } finally { + data.position(savedPosition); + data.limit(savedLimit); + } + } + + public FieldId readFieldId() { + int declaringClassIndex = readUnsignedShort(); + int typeIndex = readUnsignedShort(); + int nameIndex = readInt(); + return new FieldId(Dex.this, declaringClassIndex, typeIndex, nameIndex); + } + + public MethodId readMethodId() { + int declaringClassIndex = readUnsignedShort(); + int protoIndex = readUnsignedShort(); + int nameIndex = readInt(); + return new MethodId(Dex.this, declaringClassIndex, protoIndex, nameIndex); + } + + public ProtoId readProtoId() { + int shortyIndex = readInt(); + int returnTypeIndex = readInt(); + int parametersOffset = readInt(); + return new ProtoId(Dex.this, shortyIndex, returnTypeIndex, parametersOffset); + } + + public CallSiteId readCallSiteId() { + int offset = readInt(); + return new CallSiteId(Dex.this, offset); + } + + public MethodHandle readMethodHandle() { + MethodHandleType methodHandleType = MethodHandleType.fromValue(readUnsignedShort()); + int unused1 = readUnsignedShort(); + int fieldOrMethodId = readUnsignedShort(); + int unused2 = readUnsignedShort(); + return new MethodHandle(Dex.this, methodHandleType, unused1, fieldOrMethodId, unused2); + } + + public ClassDef readClassDef() { + int offset = getPosition(); + int type = readInt(); + int accessFlags = readInt(); + int supertype = readInt(); + int interfacesOffset = readInt(); + int sourceFileIndex = readInt(); + int annotationsOffset = readInt(); + int classDataOffset = readInt(); + int staticValuesOffset = readInt(); + return new ClassDef(Dex.this, offset, type, accessFlags, supertype, + interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset, + staticValuesOffset); + } + + private Code readCode() { + int registersSize = readUnsignedShort(); + int insSize = readUnsignedShort(); + int outsSize = readUnsignedShort(); + int triesSize = readUnsignedShort(); + int debugInfoOffset = readInt(); + int instructionsSize = readInt(); + short[] instructions = readShortArray(instructionsSize); + Try[] tries; + CatchHandler[] catchHandlers; + if (triesSize > 0) { + if (instructions.length % 2 == 1) { + readShort(); // padding + } + + /* + * We can't read the tries until we've read the catch handlers. + * Unfortunately they're in the opposite order in the dex file + * so we need to read them out-of-order. + */ + Section triesSection = open(data.position()); + skip(triesSize * SizeOf.TRY_ITEM); + catchHandlers = readCatchHandlers(); + tries = triesSection.readTries(triesSize, catchHandlers); + } else { + tries = new Try[0]; + catchHandlers = new CatchHandler[0]; + } + return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions, + tries, catchHandlers); + } + + private CatchHandler[] readCatchHandlers() { + int baseOffset = data.position(); + int catchHandlersSize = readUleb128(); + CatchHandler[] result = new CatchHandler[catchHandlersSize]; + for (int i = 0; i < catchHandlersSize; i++) { + int offset = data.position() - baseOffset; + result[i] = readCatchHandler(offset); + } + return result; + } + + private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) { + Try[] result = new Try[triesSize]; + for (int i = 0; i < triesSize; i++) { + int startAddress = readInt(); + int instructionCount = readUnsignedShort(); + int handlerOffset = readUnsignedShort(); + int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset); + result[i] = new Try(startAddress, instructionCount, catchHandlerIndex); + } + return result; + } + + private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) { + for (int i = 0; i < catchHandlers.length; i++) { + CatchHandler catchHandler = catchHandlers[i]; + if (catchHandler.getOffset() == offset) { + return i; + } + } + throw new IllegalArgumentException(); + } + + private CatchHandler readCatchHandler(int offset) { + int size = readSleb128(); + int handlersCount = Math.abs(size); + int[] typeIndexes = new int[handlersCount]; + int[] addresses = new int[handlersCount]; + for (int i = 0; i < handlersCount; i++) { + typeIndexes[i] = readUleb128(); + addresses[i] = readUleb128(); + } + int catchAllAddress = size <= 0 ? readUleb128() : -1; + return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset); + } + + private ClassData readClassData() { + int staticFieldsSize = readUleb128(); + int instanceFieldsSize = readUleb128(); + int directMethodsSize = readUleb128(); + int virtualMethodsSize = readUleb128(); + ClassData.Field[] staticFields = readFields(staticFieldsSize); + ClassData.Field[] instanceFields = readFields(instanceFieldsSize); + ClassData.Method[] directMethods = readMethods(directMethodsSize); + ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize); + return new ClassData(staticFields, instanceFields, directMethods, virtualMethods); + } + + private ClassData.Field[] readFields(int count) { + ClassData.Field[] result = new ClassData.Field[count]; + int fieldIndex = 0; + for (int i = 0; i < count; i++) { + fieldIndex += readUleb128(); // field index diff + int accessFlags = readUleb128(); + result[i] = new ClassData.Field(fieldIndex, accessFlags); + } + return result; + } + + private ClassData.Method[] readMethods(int count) { + ClassData.Method[] result = new ClassData.Method[count]; + int methodIndex = 0; + for (int i = 0; i < count; i++) { + methodIndex += readUleb128(); // method index diff + int accessFlags = readUleb128(); + int codeOff = readUleb128(); + result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff); + } + return result; + } + + /** + * Returns a byte array containing the bytes from {@code start} to this + * section's current position. + */ + private byte[] getBytesFrom(int start) { + int end = data.position(); + byte[] result = new byte[end - start]; + data.position(start); + data.get(result); + return result; + } + + public Annotation readAnnotation() { + byte visibility = readByte(); + int start = data.position(); + new EncodedValueReader(this, EncodedValueReader.ENCODED_ANNOTATION).skipValue(); + return new Annotation(Dex.this, visibility, new EncodedValue(getBytesFrom(start))); + } + + public EncodedValue readEncodedArray() { + int start = data.position(); + new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue(); + return new EncodedValue(getBytesFrom(start)); + } + + public void skip(int count) { + if (count < 0) { + throw new IllegalArgumentException(); + } + data.position(data.position() + count); + } + + /** + * Skips bytes until the position is aligned to a multiple of 4. + */ + public void alignToFourBytes() { + data.position((data.position() + 3) & ~3); + } + + /** + * Writes 0x00 until the position is aligned to a multiple of 4. + */ + public void alignToFourBytesWithZeroFill() { + while ((data.position() & 3) != 0) { + data.put((byte) 0); + } + } + + public void assertFourByteAligned() { + if ((data.position() & 3) != 0) { + throw new IllegalStateException("Not four byte aligned!"); + } + } + + public void write(byte[] bytes) { + this.data.put(bytes); + } + + @Override + public void writeByte(int b) { + data.put((byte) b); + } + + public void writeShort(short i) { + data.putShort(i); + } + + public void writeUnsignedShort(int i) { + short s = (short) i; + if (i != (s & 0xffff)) { + throw new IllegalArgumentException("Expected an unsigned short: " + i); + } + writeShort(s); + } + + public void write(short[] shorts) { + for (short s : shorts) { + writeShort(s); + } + } + + public void writeInt(int i) { + data.putInt(i); + } + + public void writeUleb128(int i) { + try { + Leb128.writeUnsignedLeb128(this, i); + } catch (ArrayIndexOutOfBoundsException e) { + throw new DexException("Section limit " + data.limit() + " exceeded by " + name); + } + } + + public void writeSleb128(int i) { + try { + Leb128.writeSignedLeb128(this, i); + } catch (ArrayIndexOutOfBoundsException e) { + throw new DexException("Section limit " + data.limit() + " exceeded by " + name); + } + } + + public void writeStringData(String value) { + try { + int length = value.length(); + writeUleb128(length); + write(Mutf8.encode(value)); + writeByte(0); + } catch (UTFDataFormatException e) { + throw new AssertionError(); + } + } + + public void writeTypeList(TypeList typeList) { + short[] types = typeList.getTypes(); + writeInt(types.length); + for (short type : types) { + writeShort(type); + } + alignToFourBytesWithZeroFill(); + } + + /** + * Returns the number of bytes used by this section. + */ + public int used() { + return data.position() - initialPosition; + } + } + + private final class StringTable extends AbstractList implements RandomAccess { + @Override public String get(int index) { + checkBounds(index, tableOfContents.stringIds.size); + return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM)) + .readString(); + } + @Override public int size() { + return tableOfContents.stringIds.size; + } + } + + private final class TypeIndexToDescriptorIndexTable extends AbstractList + implements RandomAccess { + @Override public Integer get(int index) { + return descriptorIndexFromTypeIndex(index); + } + @Override public int size() { + return tableOfContents.typeIds.size; + } + } + + private final class TypeIndexToDescriptorTable extends AbstractList + implements RandomAccess { + @Override public String get(int index) { + return strings.get(descriptorIndexFromTypeIndex(index)); + } + @Override public int size() { + return tableOfContents.typeIds.size; + } + } + + private final class ProtoIdTable extends AbstractList implements RandomAccess { + @Override public ProtoId get(int index) { + checkBounds(index, tableOfContents.protoIds.size); + return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index)) + .readProtoId(); + } + @Override public int size() { + return tableOfContents.protoIds.size; + } + } + + private final class FieldIdTable extends AbstractList implements RandomAccess { + @Override public FieldId get(int index) { + checkBounds(index, tableOfContents.fieldIds.size); + return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index)) + .readFieldId(); + } + @Override public int size() { + return tableOfContents.fieldIds.size; + } + } + + private final class MethodIdTable extends AbstractList implements RandomAccess { + @Override public MethodId get(int index) { + checkBounds(index, tableOfContents.methodIds.size); + return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index)) + .readMethodId(); + } + @Override public int size() { + return tableOfContents.methodIds.size; + } + } + + private final class ClassDefIterator implements Iterator { + private final Dex.Section in = open(tableOfContents.classDefs.off); + private int count = 0; + + @Override + public boolean hasNext() { + return count < tableOfContents.classDefs.size; + } + @Override + public ClassDef next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + count++; + return in.readClassDef(); + } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + private final class ClassDefIterable implements Iterable { + @Override + public Iterator iterator() { + return !tableOfContents.classDefs.exists() + ? Collections.emptySet().iterator() + : new ClassDefIterator(); + } + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/EncodedValueCodec.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/EncodedValueCodec.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/EncodedValueCodec.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/EncodedValueCodec.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2011 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.dex; + +import com.android.dex.util.ByteInput; +import com.android.dex.util.ByteOutput; + +/** + * Read and write {@code encoded_value} primitives. + */ +public final class EncodedValueCodec { + private EncodedValueCodec() { + } + + /** + * Writes a signed integral to {@code out}. + */ + public static void writeSignedIntegralValue(ByteOutput out, int type, long value) { + /* + * Figure out how many bits are needed to represent the value, + * including a sign bit: The bit count is subtracted from 65 + * and not 64 to account for the sign bit. The xor operation + * has the effect of leaving non-negative values alone and + * unary complementing negative values (so that a leading zero + * count always returns a useful number for our present + * purpose). + */ + int requiredBits = 65 - Long.numberOfLeadingZeros(value ^ (value >> 63)); + + // Round up the requiredBits to a number of bytes. + int requiredBytes = (requiredBits + 0x07) >> 3; + + /* + * Write the header byte, which includes the type and + * requiredBytes - 1. + */ + out.writeByte(type | ((requiredBytes - 1) << 5)); + + // Write the value, per se. + while (requiredBytes > 0) { + out.writeByte((byte) value); + value >>= 8; + requiredBytes--; + } + } + + /** + * Writes an unsigned integral to {@code out}. + */ + public static void writeUnsignedIntegralValue(ByteOutput out, int type, long value) { + // Figure out how many bits are needed to represent the value. + int requiredBits = 64 - Long.numberOfLeadingZeros(value); + if (requiredBits == 0) { + requiredBits = 1; + } + + // Round up the requiredBits to a number of bytes. + int requiredBytes = (requiredBits + 0x07) >> 3; + + /* + * Write the header byte, which includes the type and + * requiredBytes - 1. + */ + out.writeByte(type | ((requiredBytes - 1) << 5)); + + // Write the value, per se. + while (requiredBytes > 0) { + out.writeByte((byte) value); + value >>= 8; + requiredBytes--; + } + } + + /** + * Writes a right-zero-extended value to {@code out}. + */ + public static void writeRightZeroExtendedValue(ByteOutput out, int type, long value) { + // Figure out how many bits are needed to represent the value. + int requiredBits = 64 - Long.numberOfTrailingZeros(value); + if (requiredBits == 0) { + requiredBits = 1; + } + + // Round up the requiredBits to a number of bytes. + int requiredBytes = (requiredBits + 0x07) >> 3; + + // Scootch the first bits to be written down to the low-order bits. + value >>= 64 - (requiredBytes * 8); + + /* + * Write the header byte, which includes the type and + * requiredBytes - 1. + */ + out.writeByte(type | ((requiredBytes - 1) << 5)); + + // Write the value, per se. + while (requiredBytes > 0) { + out.writeByte((byte) value); + value >>= 8; + requiredBytes--; + } + } + + /** + * Read a signed integer. + * + * @param zwidth byte count minus one + */ + public static int readSignedInt(ByteInput in, int zwidth) { + int result = 0; + for (int i = zwidth; i >= 0; i--) { + result = (result >>> 8) | ((in.readByte() & 0xff) << 24); + } + result >>= (3 - zwidth) * 8; + return result; + } + + /** + * Read an unsigned integer. + * + * @param zwidth byte count minus one + * @param fillOnRight true to zero fill on the right; false on the left + */ + public static int readUnsignedInt(ByteInput in, int zwidth, boolean fillOnRight) { + int result = 0; + if (!fillOnRight) { + for (int i = zwidth; i >= 0; i--) { + result = (result >>> 8) | ((in.readByte() & 0xff) << 24); + } + result >>>= (3 - zwidth) * 8; + } else { + for (int i = zwidth; i >= 0; i--) { + result = (result >>> 8) | ((in.readByte() & 0xff) << 24); + } + } + return result; + } + + /** + * Read a signed long. + * + * @param zwidth byte count minus one + */ + public static long readSignedLong(ByteInput in, int zwidth) { + long result = 0; + for (int i = zwidth; i >= 0; i--) { + result = (result >>> 8) | ((in.readByte() & 0xffL) << 56); + } + result >>= (7 - zwidth) * 8; + return result; + } + + /** + * Read an unsigned long. + * + * @param zwidth byte count minus one + * @param fillOnRight true to zero fill on the right; false on the left + */ + public static long readUnsignedLong(ByteInput in, int zwidth, boolean fillOnRight) { + long result = 0; + if (!fillOnRight) { + for (int i = zwidth; i >= 0; i--) { + result = (result >>> 8) | ((in.readByte() & 0xffL) << 56); + } + result >>>= (7 - zwidth) * 8; + } else { + for (int i = zwidth; i >= 0; i--) { + result = (result >>> 8) | ((in.readByte() & 0xffL) << 56); + } + } + return result; + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/EncodedValue.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/EncodedValue.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/EncodedValue.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/EncodedValue.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 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.dex; + +import com.android.dex.util.ByteArrayByteInput; +import com.android.dex.util.ByteInput; + +/** + * An encoded value or array. + */ +public final class EncodedValue implements Comparable { + private final byte[] data; + + public EncodedValue(byte[] data) { + this.data = data; + } + + public ByteInput asByteInput() { + return new ByteArrayByteInput(data); + } + + public byte[] getBytes() { + return data; + } + + public void writeTo(Dex.Section out) { + out.write(data); + } + + @Override public int compareTo(EncodedValue other) { + int size = Math.min(data.length, other.data.length); + for (int i = 0; i < size; i++) { + if (data[i] != other.data[i]) { + return (data[i] & 0xff) - (other.data[i] & 0xff); + } + } + return data.length - other.data.length; + } + + @Override public String toString() { + return Integer.toHexString(data[0] & 0xff) + "...(" + data.length + ")"; + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/EncodedValueReader.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/EncodedValueReader.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/EncodedValueReader.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/EncodedValueReader.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2011 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.dex; + +import com.android.dex.util.ByteInput; + +/** + * Pull parser for encoded values. + */ +public final class EncodedValueReader { + public static final int ENCODED_BYTE = 0x00; + public static final int ENCODED_SHORT = 0x02; + public static final int ENCODED_CHAR = 0x03; + public static final int ENCODED_INT = 0x04; + public static final int ENCODED_LONG = 0x06; + public static final int ENCODED_FLOAT = 0x10; + public static final int ENCODED_DOUBLE = 0x11; + public static final int ENCODED_METHOD_TYPE = 0x15; + public static final int ENCODED_METHOD_HANDLE = 0x16; + public static final int ENCODED_STRING = 0x17; + public static final int ENCODED_TYPE = 0x18; + public static final int ENCODED_FIELD = 0x19; + public static final int ENCODED_ENUM = 0x1b; + public static final int ENCODED_METHOD = 0x1a; + public static final int ENCODED_ARRAY = 0x1c; + public static final int ENCODED_ANNOTATION = 0x1d; + public static final int ENCODED_NULL = 0x1e; + public static final int ENCODED_BOOLEAN = 0x1f; + + /** placeholder type if the type is not yet known */ + private static final int MUST_READ = -1; + + protected final ByteInput in; + private int type = MUST_READ; + private int annotationType; + private int arg; + + public EncodedValueReader(ByteInput in) { + this.in = in; + } + + public EncodedValueReader(EncodedValue in) { + this(in.asByteInput()); + } + + /** + * Creates a new encoded value reader whose only value is the specified + * known type. This is useful for encoded values without a type prefix, + * such as class_def_item's encoded_array or annotation_item's + * encoded_annotation. + */ + public EncodedValueReader(ByteInput in, int knownType) { + this.in = in; + this.type = knownType; + } + + public EncodedValueReader(EncodedValue in, int knownType) { + this(in.asByteInput(), knownType); + } + + /** + * Returns the type of the next value to read. + */ + public int peek() { + if (type == MUST_READ) { + int argAndType = in.readByte() & 0xff; + type = argAndType & 0x1f; + arg = (argAndType & 0xe0) >> 5; + } + return type; + } + + /** + * Begins reading the elements of an array, returning the array's size. The + * caller must follow up by calling a read method for each element in the + * array. For example, this reads a byte array:
   {@code
+     *   int arraySize = readArray();
+     *   for (int i = 0, i < arraySize; i++) {
+     *     readByte();
+     *   }
+     * }
+ */ + public int readArray() { + checkType(ENCODED_ARRAY); + type = MUST_READ; + return Leb128.readUnsignedLeb128(in); + } + + /** + * Begins reading the fields of an annotation, returning the number of + * fields. The caller must follow up by making alternating calls to {@link + * #readAnnotationName()} and another read method. For example, this reads + * an annotation whose fields are all bytes:
   {@code
+     *   int fieldCount = readAnnotation();
+     *   int annotationType = getAnnotationType();
+     *   for (int i = 0; i < fieldCount; i++) {
+     *       readAnnotationName();
+     *       readByte();
+     *   }
+     * }
+ */ + public int readAnnotation() { + checkType(ENCODED_ANNOTATION); + type = MUST_READ; + annotationType = Leb128.readUnsignedLeb128(in); + return Leb128.readUnsignedLeb128(in); + } + + /** + * Returns the type of the annotation just returned by {@link + * #readAnnotation()}. This method's value is undefined unless the most + * recent call was to {@link #readAnnotation()}. + */ + public int getAnnotationType() { + return annotationType; + } + + public int readAnnotationName() { + return Leb128.readUnsignedLeb128(in); + } + + public byte readByte() { + checkType(ENCODED_BYTE); + type = MUST_READ; + return (byte) EncodedValueCodec.readSignedInt(in, arg); + } + + public short readShort() { + checkType(ENCODED_SHORT); + type = MUST_READ; + return (short) EncodedValueCodec.readSignedInt(in, arg); + } + + public char readChar() { + checkType(ENCODED_CHAR); + type = MUST_READ; + return (char) EncodedValueCodec.readUnsignedInt(in, arg, false); + } + + public int readInt() { + checkType(ENCODED_INT); + type = MUST_READ; + return EncodedValueCodec.readSignedInt(in, arg); + } + + public long readLong() { + checkType(ENCODED_LONG); + type = MUST_READ; + return EncodedValueCodec.readSignedLong(in, arg); + } + + public float readFloat() { + checkType(ENCODED_FLOAT); + type = MUST_READ; + return Float.intBitsToFloat(EncodedValueCodec.readUnsignedInt(in, arg, true)); + } + + public double readDouble() { + checkType(ENCODED_DOUBLE); + type = MUST_READ; + return Double.longBitsToDouble(EncodedValueCodec.readUnsignedLong(in, arg, true)); + } + + public int readMethodType() { + checkType(ENCODED_METHOD_TYPE); + type = MUST_READ; + return EncodedValueCodec.readUnsignedInt(in, arg, false); + } + + public int readMethodHandle() { + checkType(ENCODED_METHOD_HANDLE); + type = MUST_READ; + return EncodedValueCodec.readUnsignedInt(in, arg, false); + } + + public int readString() { + checkType(ENCODED_STRING); + type = MUST_READ; + return EncodedValueCodec.readUnsignedInt(in, arg, false); + } + + public int readType() { + checkType(ENCODED_TYPE); + type = MUST_READ; + return EncodedValueCodec.readUnsignedInt(in, arg, false); + } + + public int readField() { + checkType(ENCODED_FIELD); + type = MUST_READ; + return EncodedValueCodec.readUnsignedInt(in, arg, false); + } + + public int readEnum() { + checkType(ENCODED_ENUM); + type = MUST_READ; + return EncodedValueCodec.readUnsignedInt(in, arg, false); + } + + public int readMethod() { + checkType(ENCODED_METHOD); + type = MUST_READ; + return EncodedValueCodec.readUnsignedInt(in, arg, false); + } + + public void readNull() { + checkType(ENCODED_NULL); + type = MUST_READ; + } + + public boolean readBoolean() { + checkType(ENCODED_BOOLEAN); + type = MUST_READ; + return arg != 0; + } + + /** + * Skips a single value, including its nested values if it is an array or + * annotation. + */ + public void skipValue() { + switch (peek()) { + case ENCODED_BYTE: + readByte(); + break; + case ENCODED_SHORT: + readShort(); + break; + case ENCODED_CHAR: + readChar(); + break; + case ENCODED_INT: + readInt(); + break; + case ENCODED_LONG: + readLong(); + break; + case ENCODED_FLOAT: + readFloat(); + break; + case ENCODED_DOUBLE: + readDouble(); + break; + case ENCODED_METHOD_TYPE: + readMethodType(); + break; + case ENCODED_METHOD_HANDLE: + readMethodHandle(); + break; + case ENCODED_STRING: + readString(); + break; + case ENCODED_TYPE: + readType(); + break; + case ENCODED_FIELD: + readField(); + break; + case ENCODED_ENUM: + readEnum(); + break; + case ENCODED_METHOD: + readMethod(); + break; + case ENCODED_ARRAY: + for (int i = 0, size = readArray(); i < size; i++) { + skipValue(); + } + break; + case ENCODED_ANNOTATION: + for (int i = 0, size = readAnnotation(); i < size; i++) { + readAnnotationName(); + skipValue(); + } + break; + case ENCODED_NULL: + readNull(); + break; + case ENCODED_BOOLEAN: + readBoolean(); + break; + default: + throw new DexException("Unexpected type: " + Integer.toHexString(type)); + } + } + + private void checkType(int expected) { + if (peek() != expected) { + throw new IllegalStateException( + String.format("Expected %x but was %x", expected, peek())); + } + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/FieldId.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/FieldId.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/FieldId.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/FieldId.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2011 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.dex; + +import com.android.dex.util.Unsigned; + +public final class FieldId implements Comparable { + private final Dex dex; + private final int declaringClassIndex; + private final int typeIndex; + private final int nameIndex; + + public FieldId(Dex dex, int declaringClassIndex, int typeIndex, int nameIndex) { + this.dex = dex; + this.declaringClassIndex = declaringClassIndex; + this.typeIndex = typeIndex; + this.nameIndex = nameIndex; + } + + public int getDeclaringClassIndex() { + return declaringClassIndex; + } + + public int getTypeIndex() { + return typeIndex; + } + + public int getNameIndex() { + return nameIndex; + } + + public int compareTo(FieldId other) { + if (declaringClassIndex != other.declaringClassIndex) { + return Unsigned.compare(declaringClassIndex, other.declaringClassIndex); + } + if (nameIndex != other.nameIndex) { + return Unsigned.compare(nameIndex, other.nameIndex); + } + return Unsigned.compare(typeIndex, other.typeIndex); // should always be 0 + } + + public void writeTo(Dex.Section out) { + out.writeUnsignedShort(declaringClassIndex); + out.writeUnsignedShort(typeIndex); + out.writeInt(nameIndex); + } + + @Override public String toString() { + if (dex == null) { + return declaringClassIndex + " " + typeIndex + " " + nameIndex; + } + return dex.typeNames().get(typeIndex) + "." + dex.strings().get(nameIndex); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/Leb128.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/Leb128.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/Leb128.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/Leb128.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2008 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.dex; + +import com.android.dex.util.ByteInput; +import com.android.dex.util.ByteOutput; + +/** + * Reads and writes DWARFv3 LEB 128 signed and unsigned integers. See DWARF v3 + * section 7.6. + */ +public final class Leb128 { + private Leb128() { + } + + /** + * Gets the number of bytes in the unsigned LEB128 encoding of the + * given value. + * + * @param value the value in question + * @return its write size, in bytes + */ + public static int unsignedLeb128Size(int value) { + // TODO: This could be much cleverer. + + int remaining = value >> 7; + int count = 0; + + while (remaining != 0) { + remaining >>= 7; + count++; + } + + return count + 1; + } + + /** + * Reads an signed integer from {@code in}. + */ + public static int readSignedLeb128(ByteInput in) { + int result = 0; + int cur; + int count = 0; + int signBits = -1; + + do { + cur = in.readByte() & 0xff; + result |= (cur & 0x7f) << (count * 7); + signBits <<= 7; + count++; + } while (((cur & 0x80) == 0x80) && count < 5); + + if ((cur & 0x80) == 0x80) { + throw new DexException("invalid LEB128 sequence"); + } + + // Sign extend if appropriate + if (((signBits >> 1) & result) != 0 ) { + result |= signBits; + } + + return result; + } + + /** + * Reads an unsigned integer from {@code in}. + */ + public static int readUnsignedLeb128(ByteInput in) { + int result = 0; + int cur; + int count = 0; + + do { + cur = in.readByte() & 0xff; + result |= (cur & 0x7f) << (count * 7); + count++; + } while (((cur & 0x80) == 0x80) && count < 5); + + if ((cur & 0x80) == 0x80) { + throw new DexException("invalid LEB128 sequence"); + } + + return result; + } + + /** + * Writes {@code value} as an unsigned integer to {@code out}, starting at + * {@code offset}. Returns the number of bytes written. + */ + public static void writeUnsignedLeb128(ByteOutput out, int value) { + int remaining = value >>> 7; + + while (remaining != 0) { + out.writeByte((byte) ((value & 0x7f) | 0x80)); + value = remaining; + remaining >>>= 7; + } + + out.writeByte((byte) (value & 0x7f)); + } + + /** + * Writes {@code value} as a signed integer to {@code out}, starting at + * {@code offset}. Returns the number of bytes written. + */ + public static void writeSignedLeb128(ByteOutput out, int value) { + int remaining = value >> 7; + boolean hasMore = true; + int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; + + while (hasMore) { + hasMore = (remaining != end) + || ((remaining & 1) != ((value >> 6) & 1)); + + out.writeByte((byte) ((value & 0x7f) | (hasMore ? 0x80 : 0))); + value = remaining; + remaining >>= 7; + } + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/MethodHandle.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/MethodHandle.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/MethodHandle.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/MethodHandle.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2017 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.dex; + +import com.android.dex.Dex.Section; +import com.android.dex.util.Unsigned; + +/** + * A method_handle_item: + * https://source.android.com/devices/tech/dalvik/dex-format#method-handle-item + */ +public class MethodHandle implements Comparable { + + /** + * A method handle type code: + * https://source.android.com/devices/tech/dalvik/dex-format#method-handle-type-codes + */ + public enum MethodHandleType { + METHOD_HANDLE_TYPE_STATIC_PUT(0x00), + METHOD_HANDLE_TYPE_STATIC_GET(0x01), + METHOD_HANDLE_TYPE_INSTANCE_PUT(0x02), + METHOD_HANDLE_TYPE_INSTANCE_GET(0x03), + METHOD_HANDLE_TYPE_INVOKE_STATIC(0x04), + METHOD_HANDLE_TYPE_INVOKE_INSTANCE(0x05), + METHOD_HANDLE_TYPE_INVOKE_DIRECT(0x06), + METHOD_HANDLE_TYPE_INVOKE_CONSTRUCTOR(0x07), + METHOD_HANDLE_TYPE_INVOKE_INTERFACE(0x08); + + private final int value; + + MethodHandleType(int value) { + this.value = value; + } + + static MethodHandleType fromValue(int value) { + for (MethodHandleType methodHandleType : values()) { + if (methodHandleType.value == value) { + return methodHandleType; + } + } + throw new IllegalArgumentException(String.valueOf(value)); + } + + public boolean isField() { + switch (this) { + case METHOD_HANDLE_TYPE_STATIC_PUT: + case METHOD_HANDLE_TYPE_STATIC_GET: + case METHOD_HANDLE_TYPE_INSTANCE_PUT: + case METHOD_HANDLE_TYPE_INSTANCE_GET: + return true; + default: + return false; + } + } + } + + private final Dex dex; + private final MethodHandleType methodHandleType; + private final int unused1; + private final int fieldOrMethodId; + private final int unused2; + + public MethodHandle( + Dex dex, + MethodHandleType methodHandleType, + int unused1, + int fieldOrMethodId, + int unused2) { + this.dex = dex; + this.methodHandleType = methodHandleType; + this.unused1 = unused1; + this.fieldOrMethodId = fieldOrMethodId; + this.unused2 = unused2; + } + + @Override + public int compareTo(MethodHandle o) { + if (methodHandleType != o.methodHandleType) { + return methodHandleType.compareTo(o.methodHandleType); + } + return Unsigned.compare(fieldOrMethodId, o.fieldOrMethodId); + } + + public MethodHandleType getMethodHandleType() { + return methodHandleType; + } + + public int getUnused1() { + return unused1; + } + + public int getFieldOrMethodId() { + return fieldOrMethodId; + } + + public int getUnused2() { + return unused2; + } + + public void writeTo(Section out) { + out.writeUnsignedShort(methodHandleType.value); + out.writeUnsignedShort(unused1); + out.writeUnsignedShort(fieldOrMethodId); + out.writeUnsignedShort(unused2); + } + + @Override + public String toString() { + if (dex == null) { + return methodHandleType + " " + fieldOrMethodId; + } + return methodHandleType + + " " + + (methodHandleType.isField() + ? dex.fieldIds().get(fieldOrMethodId) + : dex.methodIds().get(fieldOrMethodId)); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/MethodId.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/MethodId.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/MethodId.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/MethodId.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2011 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.dex; + +import com.android.dex.util.Unsigned; + +public final class MethodId implements Comparable { + private final Dex dex; + private final int declaringClassIndex; + private final int protoIndex; + private final int nameIndex; + + public MethodId(Dex dex, int declaringClassIndex, int protoIndex, int nameIndex) { + this.dex = dex; + this.declaringClassIndex = declaringClassIndex; + this.protoIndex = protoIndex; + this.nameIndex = nameIndex; + } + + public int getDeclaringClassIndex() { + return declaringClassIndex; + } + + public int getProtoIndex() { + return protoIndex; + } + + public int getNameIndex() { + return nameIndex; + } + + public int compareTo(MethodId other) { + if (declaringClassIndex != other.declaringClassIndex) { + return Unsigned.compare(declaringClassIndex, other.declaringClassIndex); + } + if (nameIndex != other.nameIndex) { + return Unsigned.compare(nameIndex, other.nameIndex); + } + return Unsigned.compare(protoIndex, other.protoIndex); + } + + public void writeTo(Dex.Section out) { + out.writeUnsignedShort(declaringClassIndex); + out.writeUnsignedShort(protoIndex); + out.writeInt(nameIndex); + } + + @Override public String toString() { + if (dex == null) { + return declaringClassIndex + " " + protoIndex + " " + nameIndex; + } + return dex.typeNames().get(declaringClassIndex) + + "." + dex.strings().get(nameIndex) + + dex.readTypeList(dex.protoIds().get(protoIndex).getParametersOffset()); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/Mutf8.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/Mutf8.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/Mutf8.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/Mutf8.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2011 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.dex; + +import com.android.dex.util.ByteInput; +import java.io.UTFDataFormatException; + +/** + * Modified UTF-8 as described in the dex file format spec. + * + *

Derived from libcore's MUTF-8 encoder at java.nio.charset.ModifiedUtf8. + */ +public final class Mutf8 { + private Mutf8() {} + + /** + * Decodes bytes from {@code in} into {@code out} until a delimiter 0x00 is + * encountered. Returns a new string containing the decoded characters. + */ + public static String decode(ByteInput in, char[] out) throws UTFDataFormatException { + int s = 0; + while (true) { + char a = (char) (in.readByte() & 0xff); + if (a == 0) { + return new String(out, 0, s); + } + out[s] = a; + if (a < '\u0080') { + s++; + } else if ((a & 0xe0) == 0xc0) { + int b = in.readByte() & 0xff; + if ((b & 0xC0) != 0x80) { + throw new UTFDataFormatException("bad second byte"); + } + out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F)); + } else if ((a & 0xf0) == 0xe0) { + int b = in.readByte() & 0xff; + int c = in.readByte() & 0xff; + if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) { + throw new UTFDataFormatException("bad second or third byte"); + } + out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)); + } else { + throw new UTFDataFormatException("bad byte"); + } + } + } + + /** + * Returns the number of bytes the modified UTF8 representation of 's' would take. + */ + private static long countBytes(String s, boolean shortLength) throws UTFDataFormatException { + long result = 0; + final int length = s.length(); + for (int i = 0; i < length; ++i) { + char ch = s.charAt(i); + if (ch != 0 && ch <= 127) { // U+0000 uses two bytes. + ++result; + } else if (ch <= 2047) { + result += 2; + } else { + result += 3; + } + if (shortLength && result > 65535) { + throw new UTFDataFormatException("String more than 65535 UTF bytes long"); + } + } + return result; + } + + /** + * Encodes the modified UTF-8 bytes corresponding to {@code s} into {@code + * dst}, starting at {@code offset}. + */ + public static void encode(byte[] dst, int offset, String s) { + final int length = s.length(); + for (int i = 0; i < length; i++) { + char ch = s.charAt(i); + if (ch != 0 && ch <= 127) { // U+0000 uses two bytes. + dst[offset++] = (byte) ch; + } else if (ch <= 2047) { + dst[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6))); + dst[offset++] = (byte) (0x80 | (0x3f & ch)); + } else { + dst[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12))); + dst[offset++] = (byte) (0x80 | (0x3f & (ch >> 6))); + dst[offset++] = (byte) (0x80 | (0x3f & ch)); + } + } + } + + /** + * Returns an array containing the modified UTF-8 form of {@code s}. + */ + public static byte[] encode(String s) throws UTFDataFormatException { + int utfCount = (int) countBytes(s, true); + byte[] result = new byte[utfCount]; + encode(result, 0, s); + return result; + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/ProtoId.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/ProtoId.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/ProtoId.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/ProtoId.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2011 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.dex; + +import com.android.dex.util.Unsigned; + +public final class ProtoId implements Comparable { + private final Dex dex; + private final int shortyIndex; + private final int returnTypeIndex; + private final int parametersOffset; + + public ProtoId(Dex dex, int shortyIndex, int returnTypeIndex, int parametersOffset) { + this.dex = dex; + this.shortyIndex = shortyIndex; + this.returnTypeIndex = returnTypeIndex; + this.parametersOffset = parametersOffset; + } + + public int compareTo(ProtoId other) { + if (returnTypeIndex != other.returnTypeIndex) { + return Unsigned.compare(returnTypeIndex, other.returnTypeIndex); + } + return Unsigned.compare(parametersOffset, other.parametersOffset); + } + + public int getShortyIndex() { + return shortyIndex; + } + + public int getReturnTypeIndex() { + return returnTypeIndex; + } + + public int getParametersOffset() { + return parametersOffset; + } + + public void writeTo(Dex.Section out) { + out.writeInt(shortyIndex); + out.writeInt(returnTypeIndex); + out.writeInt(parametersOffset); + } + + @Override public String toString() { + if (dex == null) { + return shortyIndex + " " + returnTypeIndex + " " + parametersOffset; + } + + return dex.strings().get(shortyIndex) + + ": " + dex.typeNames().get(returnTypeIndex) + + " " + dex.readTypeList(parametersOffset); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/SizeOf.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/SizeOf.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/SizeOf.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/SizeOf.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2011 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.dex; + +public final class SizeOf { + private SizeOf() {} + + public static final int UBYTE = 1; + public static final int USHORT = 2; + public static final int UINT = 4; + + public static final int SIGNATURE = UBYTE * 20; + + /** + * magic ubyte[8] + * checksum uint + * signature ubyte[20] + * file_size uint + * header_size uint + * endian_tag uint + * link_size uint + * link_off uint + * map_off uint + * string_ids_size uint + * string_ids_off uint + * type_ids_size uint + * type_ids_off uint + * proto_ids_size uint + * proto_ids_off uint + * field_ids_size uint + * field_ids_off uint + * method_ids_size uint + * method_ids_off uint + * class_defs_size uint + * class_defs_off uint + * data_size uint + * data_off uint + */ + public static final int HEADER_ITEM = (8 * UBYTE) + UINT + SIGNATURE + (20 * UINT); // 0x70 + + /** + * string_data_off uint + */ + public static final int STRING_ID_ITEM = UINT; + + /** + * descriptor_idx uint + */ + public static final int TYPE_ID_ITEM = UINT; + + /** + * type_idx ushort + */ + public static final int TYPE_ITEM = USHORT; + + /** + * shorty_idx uint + * return_type_idx uint + * return_type_idx uint + */ + public static final int PROTO_ID_ITEM = UINT + UINT + UINT; + + /** + * class_idx ushort + * type_idx/proto_idx ushort + * name_idx uint + */ + public static final int MEMBER_ID_ITEM = USHORT + USHORT + UINT; + + /** + * class_idx uint + * access_flags uint + * superclass_idx uint + * interfaces_off uint + * source_file_idx uint + * annotations_off uint + * class_data_off uint + * static_values_off uint + */ + public static final int CLASS_DEF_ITEM = 8 * UINT; + + /** + * type ushort + * unused ushort + * size uint + * offset uint + */ + public static final int MAP_ITEM = USHORT + USHORT + UINT + UINT; + + /** + * start_addr uint + * insn_count ushort + * handler_off ushort + */ + public static final int TRY_ITEM = UINT + USHORT + USHORT; + + /** + * call_site_off uint + */ + public static final int CALL_SITE_ID_ITEM = UINT; + + /** + * method_handle_type ushort + * unused ushort + * field_or_method_id ushort + * unused ushort + */ + public static final int METHOD_HANDLE_ITEM = USHORT + USHORT + USHORT + USHORT; +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/TableOfContents.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/TableOfContents.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/TableOfContents.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/TableOfContents.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2011 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.dex; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; + +/** + * The file header and map. + */ +public final class TableOfContents { + + /* + * TODO: factor out ID constants. + */ + + public final Section header = new Section(0x0000); + public final Section stringIds = new Section(0x0001); + public final Section typeIds = new Section(0x0002); + public final Section protoIds = new Section(0x0003); + public final Section fieldIds = new Section(0x0004); + public final Section methodIds = new Section(0x0005); + public final Section classDefs = new Section(0x0006); + public final Section callSiteIds = new Section(0x0007); + public final Section methodHandles = new Section(0x0008); + public final Section mapList = new Section(0x1000); + public final Section typeLists = new Section(0x1001); + public final Section annotationSetRefLists = new Section(0x1002); + public final Section annotationSets = new Section(0x1003); + public final Section classDatas = new Section(0x2000); + public final Section codes = new Section(0x2001); + public final Section stringDatas = new Section(0x2002); + public final Section debugInfos = new Section(0x2003); + public final Section annotations = new Section(0x2004); + public final Section encodedArrays = new Section(0x2005); + public final Section annotationsDirectories = new Section(0x2006); + public final Section[] sections = { + header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList, callSiteIds, + methodHandles, typeLists, annotationSetRefLists, annotationSets, classDatas, codes, + stringDatas, debugInfos, annotations, encodedArrays, annotationsDirectories + }; + + public int apiLevel; + public int checksum; + public byte[] signature; + public int fileSize; + public int linkSize; + public int linkOff; + public int dataSize; + public int dataOff; + + public TableOfContents() { + signature = new byte[20]; + } + + public void readFrom(Dex dex) throws IOException { + readHeader(dex.open(0)); + readMap(dex.open(mapList.off)); + computeSizesFromOffsets(); + } + + private void readHeader(Dex.Section headerIn) throws UnsupportedEncodingException { + byte[] magic = headerIn.readByteArray(8); + + if (!DexFormat.isSupportedDexMagic(magic)) { + String msg = + String.format("Unexpected magic: [0x%02x, 0x%02x, 0x%02x, 0x%02x, " + + "0x%02x, 0x%02x, 0x%02x, 0x%02x]", + magic[0], magic[1], magic[2], magic[3], + magic[4], magic[5], magic[6], magic[7]); + throw new DexException(msg); + } + + apiLevel = DexFormat.magicToApi(magic); + checksum = headerIn.readInt(); + signature = headerIn.readByteArray(20); + fileSize = headerIn.readInt(); + int headerSize = headerIn.readInt(); + if (headerSize != SizeOf.HEADER_ITEM) { + throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize)); + } + int endianTag = headerIn.readInt(); + if (endianTag != DexFormat.ENDIAN_TAG) { + throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag)); + } + linkSize = headerIn.readInt(); + linkOff = headerIn.readInt(); + mapList.off = headerIn.readInt(); + if (mapList.off == 0) { + throw new DexException("Cannot merge dex files that do not contain a map"); + } + stringIds.size = headerIn.readInt(); + stringIds.off = headerIn.readInt(); + typeIds.size = headerIn.readInt(); + typeIds.off = headerIn.readInt(); + protoIds.size = headerIn.readInt(); + protoIds.off = headerIn.readInt(); + fieldIds.size = headerIn.readInt(); + fieldIds.off = headerIn.readInt(); + methodIds.size = headerIn.readInt(); + methodIds.off = headerIn.readInt(); + classDefs.size = headerIn.readInt(); + classDefs.off = headerIn.readInt(); + dataSize = headerIn.readInt(); + dataOff = headerIn.readInt(); + } + + private void readMap(Dex.Section in) throws IOException { + int mapSize = in.readInt(); + Section previous = null; + for (int i = 0; i < mapSize; i++) { + short type = in.readShort(); + in.readShort(); // unused + Section section = getSection(type); + int size = in.readInt(); + int offset = in.readInt(); + + if ((section.size != 0 && section.size != size) + || (section.off != -1 && section.off != offset)) { + throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type)); + } + + section.size = size; + section.off = offset; + + if (previous != null && previous.off > section.off) { + throw new DexException("Map is unsorted at " + previous + ", " + section); + } + + previous = section; + } + Arrays.sort(sections); + } + + public void computeSizesFromOffsets() { + int end = dataOff + dataSize; + for (int i = sections.length - 1; i >= 0; i--) { + Section section = sections[i]; + if (section.off == -1) { + continue; + } + if (section.off > end) { + throw new DexException("Map is unsorted at " + section); + } + section.byteCount = end - section.off; + end = section.off; + } + } + + private Section getSection(short type) { + for (Section section : sections) { + if (section.type == type) { + return section; + } + } + throw new IllegalArgumentException("No such map item: " + type); + } + + public void writeHeader(Dex.Section out, int api) throws IOException { + out.write(DexFormat.apiToMagic(api).getBytes("UTF-8")); + out.writeInt(checksum); + out.write(signature); + out.writeInt(fileSize); + out.writeInt(SizeOf.HEADER_ITEM); + out.writeInt(DexFormat.ENDIAN_TAG); + out.writeInt(linkSize); + out.writeInt(linkOff); + out.writeInt(mapList.off); + out.writeInt(stringIds.size); + out.writeInt(stringIds.off); + out.writeInt(typeIds.size); + out.writeInt(typeIds.off); + out.writeInt(protoIds.size); + out.writeInt(protoIds.off); + out.writeInt(fieldIds.size); + out.writeInt(fieldIds.off); + out.writeInt(methodIds.size); + out.writeInt(methodIds.off); + out.writeInt(classDefs.size); + out.writeInt(classDefs.off); + out.writeInt(dataSize); + out.writeInt(dataOff); + } + + public void writeMap(Dex.Section out) throws IOException { + int count = 0; + for (Section section : sections) { + if (section.exists()) { + count++; + } + } + + out.writeInt(count); + for (Section section : sections) { + if (section.exists()) { + out.writeShort(section.type); + out.writeShort((short) 0); + out.writeInt(section.size); + out.writeInt(section.off); + } + } + } + + public static class Section implements Comparable

{ + public final short type; + public int size = 0; + public int off = -1; + public int byteCount = 0; + + public Section(int type) { + this.type = (short) type; + } + + public boolean exists() { + return size > 0; + } + + public int compareTo(Section section) { + if (off != section.off) { + return off < section.off ? -1 : 1; + } + return 0; + } + + @Override public String toString() { + return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size); + } + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/TypeList.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/TypeList.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/TypeList.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/TypeList.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 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.dex; + +import com.android.dex.util.Unsigned; + +public final class TypeList implements Comparable { + + public static final TypeList EMPTY = new TypeList(null, Dex.EMPTY_SHORT_ARRAY); + + private final Dex dex; + private final short[] types; + + public TypeList(Dex dex, short[] types) { + this.dex = dex; + this.types = types; + } + + public short[] getTypes() { + return types; + } + + @Override public int compareTo(TypeList other) { + for (int i = 0; i < types.length && i < other.types.length; i++) { + if (types[i] != other.types[i]) { + return Unsigned.compare(types[i], other.types[i]); + } + } + return Unsigned.compare(types.length, other.types.length); + } + + @Override public String toString() { + StringBuilder result = new StringBuilder(); + result.append("("); + for (int i = 0, typesLength = types.length; i < typesLength; i++) { + result.append(dex != null ? dex.typeNames().get(types[i]) : types[i]); + } + result.append(")"); + return result.toString(); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/ByteArrayByteInput.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/ByteArrayByteInput.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/ByteArrayByteInput.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/ByteArrayByteInput.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 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.dex.util; + +public final class ByteArrayByteInput implements ByteInput { + + private final byte[] bytes; + private int position; + + public ByteArrayByteInput(byte... bytes) { + this.bytes = bytes; + } + + @Override public byte readByte() { + return bytes[position++]; + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/ByteInput.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/ByteInput.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/ByteInput.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/ByteInput.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 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.dex.util; + +/** + * A byte source. + */ +public interface ByteInput { + + /** + * Returns a byte. + * + * @throws IndexOutOfBoundsException if all bytes have been read. + */ + byte readByte(); +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/ByteOutput.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/ByteOutput.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/ByteOutput.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/ByteOutput.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 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.dex.util; + +/** + * A byte sink. + */ +public interface ByteOutput { + + /** + * Writes a byte. + * + * @throws IndexOutOfBoundsException if all bytes have been written. + */ + void writeByte(int i); +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/ExceptionWithContext.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/ExceptionWithContext.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/ExceptionWithContext.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/ExceptionWithContext.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2007 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.dex.util; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * Exception which carries around structured context. + */ +public class ExceptionWithContext extends RuntimeException { + /** {@code non-null;} human-oriented context of the exception */ + private StringBuffer context; + + /** + * Augments the given exception with the given context, and return the + * result. The result is either the given exception if it was an + * {@link ExceptionWithContext}, or a newly-constructed exception if it + * was not. + * + * @param ex {@code non-null;} the exception to augment + * @param str {@code non-null;} context to add + * @return {@code non-null;} an appropriate instance + */ + public static ExceptionWithContext withContext(Throwable ex, String str) { + ExceptionWithContext ewc; + + if (ex instanceof ExceptionWithContext) { + ewc = (ExceptionWithContext) ex; + } else { + ewc = new ExceptionWithContext(ex); + } + + ewc.addContext(str); + return ewc; + } + + /** + * Constructs an instance. + * + * @param message human-oriented message + */ + public ExceptionWithContext(String message) { + this(message, null); + } + + /** + * Constructs an instance. + * + * @param cause {@code null-ok;} exception that caused this one + */ + public ExceptionWithContext(Throwable cause) { + this(null, cause); + } + + /** + * Constructs an instance. + * + * @param message human-oriented message + * @param cause {@code null-ok;} exception that caused this one + */ + public ExceptionWithContext(String message, Throwable cause) { + super((message != null) ? message : + (cause != null) ? cause.getMessage() : null, + cause); + + if (cause instanceof ExceptionWithContext) { + String ctx = ((ExceptionWithContext) cause).context.toString(); + context = new StringBuffer(ctx.length() + 200); + context.append(ctx); + } else { + context = new StringBuffer(200); + } + } + + /** {@inheritDoc} */ + @Override + public void printStackTrace(PrintStream out) { + super.printStackTrace(out); + out.println(context); + } + + /** {@inheritDoc} */ + @Override + public void printStackTrace(PrintWriter out) { + super.printStackTrace(out); + out.println(context); + } + + /** + * Adds a line of context to this instance. + * + * @param str {@code non-null;} new context + */ + public void addContext(String str) { + if (str == null) { + throw new NullPointerException("str == null"); + } + + context.append(str); + if (!str.endsWith("\n")) { + context.append('\n'); + } + } + + /** + * Gets the context. + * + * @return {@code non-null;} the context + */ + public String getContext() { + return context.toString(); + } + + /** + * Prints the message and context. + * + * @param out {@code non-null;} where to print to + */ + public void printContext(PrintStream out) { + out.println(getMessage()); + out.print(context); + } + + /** + * Prints the message and context. + * + * @param out {@code non-null;} where to print to + */ + public void printContext(PrintWriter out) { + out.println(getMessage()); + out.print(context); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/FileUtils.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/FileUtils.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/FileUtils.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/FileUtils.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2007 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.dex.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +/** + * File I/O utilities. + */ +public final class FileUtils { + private FileUtils() { + } + + /** + * Reads the named file, translating {@link IOException} to a + * {@link RuntimeException} of some sort. + * + * @param fileName {@code non-null;} name of the file to read + * @return {@code non-null;} contents of the file + */ + public static byte[] readFile(String fileName) { + File file = new File(fileName); + return readFile(file); + } + + /** + * Reads the given file, translating {@link IOException} to a + * {@link RuntimeException} of some sort. + * + * @param file {@code non-null;} the file to read + * @return {@code non-null;} contents of the file + */ + public static byte[] readFile(File file) { + if (!file.exists()) { + throw new RuntimeException(file + ": file not found"); + } + + if (!file.isFile()) { + throw new RuntimeException(file + ": not a file"); + } + + if (!file.canRead()) { + throw new RuntimeException(file + ": file not readable"); + } + + long longLength = file.length(); + int length = (int) longLength; + if (length != longLength) { + throw new RuntimeException(file + ": file too long"); + } + + byte[] result = new byte[length]; + + try { + FileInputStream in = new FileInputStream(file); + int at = 0; + while (length > 0) { + int amt = in.read(result, at, length); + if (amt == -1) { + throw new RuntimeException(file + ": unexpected EOF"); + } + at += amt; + length -= amt; + } + in.close(); + } catch (IOException ex) { + throw new RuntimeException(file + ": trouble reading", ex); + } + + return result; + } + + /** + * Returns true if {@code fileName} names a .zip, .jar, or .apk. + */ + public static boolean hasArchiveSuffix(String fileName) { + return fileName.endsWith(".zip") + || fileName.endsWith(".jar") + || fileName.endsWith(".apk"); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/Unsigned.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/Unsigned.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dex/util/Unsigned.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dex/util/Unsigned.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 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.dex.util; + +/** + * Unsigned arithmetic over Java's signed types. + */ +public final class Unsigned { + private Unsigned() {} + + public static int compare(short ushortA, short ushortB) { + if (ushortA == ushortB) { + return 0; + } + int a = ushortA & 0xFFFF; + int b = ushortB & 0xFFFF; + return a < b ? -1 : 1; + } + + public static int compare(int uintA, int uintB) { + if (uintA == uintB) { + return 0; + } + long a = uintA & 0xFFFFFFFFL; + long b = uintB & 0xFFFFFFFFL; + return a < b ? -1 : 1; + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/attrib/AttBootstrapMethods.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/attrib/AttBootstrapMethods.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/attrib/AttBootstrapMethods.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/attrib/AttBootstrapMethods.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 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.dx.cf.attrib; + +import com.android.dx.cf.code.BootstrapMethodsList; + +/** + * Attribute class for standard {@code AttBootstrapMethods} attributes. + */ +public class AttBootstrapMethods extends BaseAttribute { + /** {@code non-null;} attribute name for attributes of this type */ + public static final String ATTRIBUTE_NAME = "BootstrapMethods"; + + private static final int ATTRIBUTE_HEADER_BYTES = 8; + private static final int BOOTSTRAP_METHOD_BYTES = 4; + private static final int BOOTSTRAP_ARGUMENT_BYTES = 2; + + private final BootstrapMethodsList bootstrapMethods; + + private final int byteLength; + + public AttBootstrapMethods(BootstrapMethodsList bootstrapMethods) { + super(ATTRIBUTE_NAME); + this.bootstrapMethods = bootstrapMethods; + + int bytes = ATTRIBUTE_HEADER_BYTES + bootstrapMethods.size() * BOOTSTRAP_METHOD_BYTES; + for (int i = 0; i < bootstrapMethods.size(); ++i) { + int numberOfArguments = bootstrapMethods.get(i).getBootstrapMethodArguments().size(); + bytes += numberOfArguments * BOOTSTRAP_ARGUMENT_BYTES; + } + this.byteLength = bytes; + } + + @Override + public int byteLength() { + return byteLength; + } + + /** + * Get the bootstrap methods present in attribute. + * @return bootstrap methods list + */ + public BootstrapMethodsList getBootstrapMethods() { + return bootstrapMethods; + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/attrib/AttSourceDebugExtension.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/attrib/AttSourceDebugExtension.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/attrib/AttSourceDebugExtension.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/attrib/AttSourceDebugExtension.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 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.dx.cf.attrib; + +import com.android.dx.rop.cst.CstString; + +/** + * Attribute class for standard {@code SourceDebugExtension} attributes. + */ +public final class AttSourceDebugExtension extends BaseAttribute { + /** {@code non-null;} attribute name for attributes of this type */ + public static final String ATTRIBUTE_NAME = "SourceDebugExtension"; + + /** {@code non-null;} Contents of SMAP */ + private final CstString smapString; + + /** + * Constructs an instance. + * + * @param smapString {@code non-null;} the SMAP data from the class file. + */ + public AttSourceDebugExtension(CstString smapString) { + super(ATTRIBUTE_NAME); + + if (smapString == null) { + throw new NullPointerException("smapString == null"); + } + + this.smapString = smapString; + } + + /** {@inheritDoc} */ + @Override + public int byteLength() { + // Add 6 for the standard attribute header: the attribute name + // index (2 bytes) and the attribute length (4 bytes). + return 6 + smapString.getUtf8Size(); + } + + /** + * Gets the SMAP data of this instance. + * + * @return {@code non-null;} the SMAP data. + */ + public CstString getSmapString() { + return smapString; + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/BasicBlocker.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/BasicBlocker.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/BasicBlocker.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/BasicBlocker.java 2018-10-24 12:07:51.000000000 +0000 @@ -17,6 +17,7 @@ package com.android.dx.cf.code; import com.android.dx.rop.cst.Constant; +import com.android.dx.rop.cst.CstInvokeDynamic; import com.android.dx.rop.cst.CstMemberRef; import com.android.dx.rop.cst.CstString; import com.android.dx.rop.cst.CstType; @@ -200,7 +201,7 @@ visitCommon(offset, length, true); if ((cst instanceof CstMemberRef) || (cst instanceof CstType) || - (cst instanceof CstString)) { + (cst instanceof CstString) || (cst instanceof CstInvokeDynamic)) { /* * Instructions with these sorts of constants have the * possibility of throwing, so this instruction needs to diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/BootstrapMethodArgumentsList.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/BootstrapMethodArgumentsList.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/BootstrapMethodArgumentsList.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/BootstrapMethodArgumentsList.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 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.dx.cf.code; + +import com.android.dx.rop.cst.Constant; +import com.android.dx.rop.cst.CstDouble; +import com.android.dx.rop.cst.CstFloat; +import com.android.dx.rop.cst.CstInteger; +import com.android.dx.rop.cst.CstLong; +import com.android.dx.rop.cst.CstMethodHandle; +import com.android.dx.rop.cst.CstProtoRef; +import com.android.dx.rop.cst.CstString; +import com.android.dx.rop.cst.CstType; +import com.android.dx.util.FixedSizeList; + +/** + * List of bootstrap method arguments, which are part of the contents of + * {@code BootstrapMethods} attributes. + */ +public class BootstrapMethodArgumentsList extends FixedSizeList { + /** + * Constructs an instance. + * + * @param count the number of elements to be in the list + */ + public BootstrapMethodArgumentsList(int count) { + super(count); + } + + /** + * Gets the bootstrap argument from the indicated position. + * + * @param n position of argument to get + * @return {@code Constant} instance + */ + public Constant get(int n) { + return (Constant) get0(n); + } + + /** + * Sets the bootstrap argument at the indicated position. + * + * @param n position of argument to set + * @param cst {@code Constant} instance + */ + public void set(int n, Constant cst) { + // The set of permitted types is defined by the JVMS 8, section 4.7.23. + if (cst instanceof CstString || + cst instanceof CstType || + cst instanceof CstInteger || + cst instanceof CstLong || + cst instanceof CstFloat || + cst instanceof CstDouble || + cst instanceof CstMethodHandle || + cst instanceof CstProtoRef) { + set0(n, cst); + } else { + Class klass = cst.getClass(); + throw new IllegalArgumentException("bad type for bootstrap argument: " + klass); + } + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/BootstrapMethodsList.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/BootstrapMethodsList.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/BootstrapMethodsList.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/BootstrapMethodsList.java 2018-10-24 12:07:51.000000000 +0000 @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2017 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.dx.cf.code; + +import com.android.dx.rop.cst.CstMethodHandle; +import com.android.dx.rop.cst.CstType; +import com.android.dx.util.FixedSizeList; + +/** + * List of bootstrap method entries, which are the contents of + * {@code BootstrapMethods} attributes. + */ +public class BootstrapMethodsList extends FixedSizeList { + /** {@code non-null;} zero-size instance */ + public static final BootstrapMethodsList EMPTY = new BootstrapMethodsList(0); + + /** + * Constructs an instance. + * + * @param count the number of elements to be in the list + */ + public BootstrapMethodsList(int count) { + super(count); + } + + /** + * Gets the indicated item. + * + * @param n {@code >= 0;} which item + * @return {@code null-ok;} the indicated item + */ + public Item get(int n) { + return (Item) get0(n); + } + + /** + * Sets the item at the given index. + * + * @param n {@code >= 0, < size();} which element + * @param item {@code non-null;} the item + */ + public void set(int n, Item item) { + if (item == null) { + throw new NullPointerException("item == null"); + } + + set0(n, item); + } + + /** + * Sets the item at the given index. + * + * @param n {@code >= 0, < size();} which element + * @param declaringClass {@code non-null;} the class declaring bootstrap method. + * @param bootstrapMethodHandle {@code non-null;} the bootstrap method handle + * @param arguments {@code non-null;} the arguments of the bootstrap method + */ + public void set(int n, CstType declaringClass, CstMethodHandle bootstrapMethodHandle, + BootstrapMethodArgumentsList arguments) { + set(n, new Item(declaringClass, bootstrapMethodHandle, arguments)); + } + + /** + * Returns an instance which is the concatenation of the two given + * instances. + * + * @param list1 {@code non-null;} first instance + * @param list2 {@code non-null;} second instance + * @return {@code non-null;} combined instance + */ + public static BootstrapMethodsList concat(BootstrapMethodsList list1, + BootstrapMethodsList list2) { + if (list1 == EMPTY) { + return list2; + } else if (list2 == EMPTY) { + return list1; + } + + int sz1 = list1.size(); + int sz2 = list2.size(); + BootstrapMethodsList result = new BootstrapMethodsList(sz1 + sz2); + + for (int i = 0; i < sz1; i++) { + result.set(i, list1.get(i)); + } + + for (int i = 0; i < sz2; i++) { + result.set(sz1 + i, list2.get(i)); + } + + return result; + } + + public static class Item { + private final BootstrapMethodArgumentsList bootstrapMethodArgumentsList; + private final CstMethodHandle bootstrapMethodHandle; + private final CstType declaringClass; + + public Item(CstType declaringClass, CstMethodHandle bootstrapMethodHandle, + BootstrapMethodArgumentsList bootstrapMethodArguments) { + if (declaringClass == null) { + throw new NullPointerException("declaringClass == null"); + } + if (bootstrapMethodHandle == null) { + throw new NullPointerException("bootstrapMethodHandle == null"); + } + if (bootstrapMethodArguments == null) { + throw new NullPointerException("bootstrapMethodArguments == null"); + } + this.bootstrapMethodHandle = bootstrapMethodHandle; + this.bootstrapMethodArgumentsList = bootstrapMethodArguments; + this.declaringClass = declaringClass; + } + + public CstMethodHandle getBootstrapMethodHandle() { + return bootstrapMethodHandle; + } + + public BootstrapMethodArgumentsList getBootstrapMethodArguments() { + return bootstrapMethodArgumentsList; + } + + public CstType getDeclaringClass() { + return declaringClass; + } + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/BytecodeArray.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/BytecodeArray.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/BytecodeArray.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/BytecodeArray.java 2018-10-24 12:07:51.000000000 +0000 @@ -16,12 +16,13 @@ package com.android.dx.cf.code; -import com.android.dx.cf.iface.ParseException; import com.android.dx.rop.cst.Constant; import com.android.dx.rop.cst.ConstantPool; +import com.android.dx.rop.cst.CstCallSiteRef; import com.android.dx.rop.cst.CstDouble; import com.android.dx.rop.cst.CstFloat; import com.android.dx.rop.cst.CstInteger; +import com.android.dx.rop.cst.CstInvokeDynamic; import com.android.dx.rop.cst.CstKnownNull; import com.android.dx.rop.cst.CstLiteralBits; import com.android.dx.rop.cst.CstLong; @@ -774,7 +775,11 @@ return 5; } case ByteOps.INVOKEDYNAMIC: { - throw new ParseException("invokedynamic not supported"); + int idx = bytes.getUnsignedShort(offset + 1); + // Skip to must-be-zero bytes at offsets 3 and 4 + CstInvokeDynamic cstInvokeDynamic = (CstInvokeDynamic) pool.get(idx); + visitor.visitConstant(opcode, offset, 5, cstInvokeDynamic, 0); + return 5; } case ByteOps.NEWARRAY: { return parseNewarray(offset, visitor); diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/ConcreteMethod.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/ConcreteMethod.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/ConcreteMethod.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/ConcreteMethod.java 2018-10-24 12:07:51.000000000 +0000 @@ -46,12 +46,6 @@ */ private final CstString sourceFile; - /** - * whether the class that this method is part of is defined with - * {@code ACC_SUPER} - */ - private final boolean accSuper; - /** {@code non-null;} the code attribute */ private final AttCode attCode; @@ -72,13 +66,12 @@ * information (if any) */ public ConcreteMethod(Method method, ClassFile cf, boolean keepLines, boolean keepLocals) { - this(method, cf.getAccessFlags(), cf.getSourceFile(), keepLines, keepLocals); + this(method, cf.getSourceFile(), keepLines, keepLocals); } - public ConcreteMethod(Method method, int accessFlags, CstString sourceFile, + public ConcreteMethod(Method method, CstString sourceFile, boolean keepLines, boolean keepLocals) { this.method = method; - this.accSuper = (accessFlags & AccessFlags.ACC_SUPER) != 0; this.sourceFile = sourceFile; AttributeList attribs = method.getAttributes(); @@ -180,16 +173,6 @@ } /** - * Gets whether the class that this method is part of is defined with - * {@code ACC_SUPER}. - * - * @return the {@code ACC_SUPER} value - */ - public boolean getAccSuper() { - return accSuper; - } - - /** * Gets the maximum stack size. * * @return {@code >= 0;} the maximum stack size diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/LocalsArraySet.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/LocalsArraySet.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/LocalsArraySet.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/LocalsArraySet.java 2018-10-24 12:07:51.000000000 +0000 @@ -99,7 +99,7 @@ } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public void setImmutable() { primary.setImmutable(); @@ -112,13 +112,13 @@ super.setImmutable(); } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public LocalsArray copy() { return new LocalsArraySet(this); } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public void annotate(ExceptionWithContext ex) { ex.addContext("(locals array set; primary)"); @@ -137,7 +137,7 @@ } } - /** {@inheritDoc*/ + /** {@inheritDoc} */ public String toHuman() { StringBuilder sb = new StringBuilder(); @@ -162,7 +162,7 @@ return sb.toString(); } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public void makeInitialized(Type type) { int len = primary.getMaxLocals(); @@ -183,13 +183,13 @@ } } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public int getMaxLocals() { return primary.getMaxLocals(); } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public void set(int idx, TypeBearer type) { throwIfImmutable(); @@ -203,13 +203,13 @@ } } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public void set(RegisterSpec spec) { set(spec.getReg(), spec); } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public void invalidate(int idx) { throwIfImmutable(); @@ -223,25 +223,25 @@ } } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public TypeBearer getOrNull(int idx) { return primary.getOrNull(idx); } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public TypeBearer get(int idx) { return primary.get(idx); } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public TypeBearer getCategory1(int idx) { return primary.getCategory1(idx); } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public TypeBearer getCategory2(int idx) { return primary.getCategory2(idx); @@ -339,7 +339,7 @@ return new LocalsArraySet(newPrimary, newSecondaries); } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public LocalsArraySet merge(LocalsArray other) { LocalsArraySet result; diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/OneLocalsArray.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/OneLocalsArray.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/OneLocalsArray.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/OneLocalsArray.java 2018-10-24 12:07:51.000000000 +0000 @@ -45,7 +45,7 @@ locals = new TypeBearer[maxLocals]; } - /** @inheritDoc */ + /** {@inheritDoc} */ public OneLocalsArray copy() { OneLocalsArray result = new OneLocalsArray(locals.length); @@ -54,7 +54,7 @@ return result; } - /** @inheritDoc */ + /** {@inheritDoc} */ public void annotate(ExceptionWithContext ex) { for (int i = 0; i < locals.length; i++) { TypeBearer type = locals[i]; @@ -63,7 +63,7 @@ } } - /** {@inheritDoc*/ + /** {@inheritDoc} */ public String toHuman() { StringBuilder sb = new StringBuilder(); @@ -76,7 +76,7 @@ return sb.toString(); } - /** @inheritDoc */ + /** {@inheritDoc} */ public void makeInitialized(Type type) { int len = locals.length; @@ -96,12 +96,12 @@ } } - /** @inheritDoc */ + /** {@inheritDoc} */ public int getMaxLocals() { return locals.length; } - /** @inheritDoc */ + /** {@inheritDoc} */ public void set(int idx, TypeBearer type) { throwIfImmutable(); @@ -131,23 +131,23 @@ } } - /** @inheritDoc */ + /** {@inheritDoc} */ public void set(RegisterSpec spec) { set(spec.getReg(), spec); } - /** @inheritDoc */ + /** {@inheritDoc} */ public void invalidate(int idx) { throwIfImmutable(); locals[idx] = null; } - /** @inheritDoc */ + /** {@inheritDoc} */ public TypeBearer getOrNull(int idx) { return locals[idx]; } - /** @inheritDoc */ + /** {@inheritDoc} */ public TypeBearer get(int idx) { TypeBearer result = locals[idx]; @@ -158,7 +158,7 @@ return result; } - /** @inheritDoc */ + /** {@inheritDoc} */ public TypeBearer getCategory1(int idx) { TypeBearer result = get(idx); Type type = result.getType(); @@ -174,7 +174,7 @@ return result; } - /** @inheritDoc */ + /** {@inheritDoc} */ public TypeBearer getCategory2(int idx) { TypeBearer result = get(idx); @@ -185,7 +185,7 @@ return result; } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public LocalsArray merge(LocalsArray other) { if (other instanceof OneLocalsArray) { @@ -217,7 +217,7 @@ } } - /** @inheritDoc */ + /** {@inheritDoc} */ @Override public LocalsArraySet mergeWithSubroutineCaller (LocalsArray other, int predLabel) { diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/Ropper.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/Ropper.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/Ropper.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/Ropper.java 2018-10-24 12:07:52.000000000 +0000 @@ -17,6 +17,7 @@ package com.android.dx.cf.code; import com.android.dx.cf.iface.MethodList; +import com.android.dx.dex.DexOptions; import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.code.BasicBlock; import com.android.dx.rop.code.BasicBlockList; @@ -343,9 +344,9 @@ * @return {@code non-null;} the converted instance */ public static RopMethod convert(ConcreteMethod method, - TranslationAdvice advice, MethodList methods) { + TranslationAdvice advice, MethodList methods, DexOptions dexOptions) { try { - Ropper r = new Ropper(method, advice, methods); + Ropper r = new Ropper(method, advice, methods, dexOptions); r.doit(); return r.getRopMethod(); } catch (SimException ex) { @@ -363,8 +364,10 @@ * @param advice {@code non-null;} translation advice to use * @param methods {@code non-null;} list of methods defined by the class * that defines {@code method}. + * @param dexOptions {@code non-null;} options for dex output */ - private Ropper(ConcreteMethod method, TranslationAdvice advice, MethodList methods) { + private Ropper(ConcreteMethod method, TranslationAdvice advice, MethodList methods, + DexOptions dexOptions) { if (method == null) { throw new NullPointerException("method == null"); } @@ -378,7 +381,7 @@ this.maxLabel = blocks.getMaxLabel(); this.maxLocals = method.getMaxLocals(); this.machine = new RopperMachine(this, method, advice, methods); - this.sim = new Simulator(machine, method); + this.sim = new Simulator(machine, method, dexOptions); this.startFrames = new Frame[maxLabel]; this.subroutines = new Subroutine[maxLabel]; diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/RopperMachine.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/RopperMachine.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/RopperMachine.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/RopperMachine.java 2018-10-24 12:07:52.000000000 +0000 @@ -21,6 +21,7 @@ import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.code.FillArrayDataInsn; import com.android.dx.rop.code.Insn; +import com.android.dx.rop.code.InvokePolymorphicInsn; import com.android.dx.rop.code.PlainCstInsn; import com.android.dx.rop.code.PlainInsn; import com.android.dx.rop.code.RegOps; @@ -34,6 +35,7 @@ import com.android.dx.rop.code.ThrowingInsn; import com.android.dx.rop.code.TranslationAdvice; import com.android.dx.rop.cst.Constant; +import com.android.dx.rop.cst.CstCallSiteRef; import com.android.dx.rop.cst.CstFieldRef; import com.android.dx.rop.cst.CstInteger; import com.android.dx.rop.cst.CstMethodRef; @@ -497,9 +499,14 @@ */ extraBlockCount++; - moveResult = new PlainInsn( - Rops.opMoveResult(((CstMethodRef) cst).getPrototype() - .getReturnType()), pos, dest, RegisterSpecList.EMPTY); + Type returnType; + if (rop.getOpcode() == RegOps.INVOKE_CUSTOM) { + returnType = ((CstCallSiteRef) cst).getReturnType(); + } else { + returnType = ((CstMethodRef) cst).getPrototype().getReturnType(); + } + moveResult = new PlainInsn(Rops.opMoveResult(returnType), + pos, dest, RegisterSpecList.EMPTY); dest = null; } else if (dest != null && rop.canThrow()) { @@ -606,8 +613,11 @@ returns = true; } else if (cst != null) { if (canThrow) { - insn = - new ThrowingCstInsn(rop, pos, sources, catches, cst); + if (rop.getOpcode() == RegOps.INVOKE_POLYMORPHIC) { + insn = makeInvokePolymorphicInsn(rop, pos, sources, catches, cst); + } else { + insn = new ThrowingCstInsn(rop, pos, sources, catches, cst); + } catchesUsed = true; primarySuccessorIndex = catches.size(); } else { @@ -762,7 +772,7 @@ /** * Gets the register opcode for the given Java opcode. * - * @param jop {@code >= 0;} the Java opcode + * @param jop {@code jop >= 0;} the Java opcode * @param cst {@code null-ok;} the constant argument, if any * @return {@code >= 0;} the corresponding register opcode */ @@ -949,6 +959,12 @@ } } } + // If the method reference is a signature polymorphic method + // substitute invoke-polymorphic for invoke-virtual. This only + // affects MethodHandle.invoke and MethodHandle.invokeExact. + if (ref.isSignaturePolymorphic()) { + return RegOps.INVOKE_POLYMORPHIC; + } return RegOps.INVOKE_VIRTUAL; } case ByteOps.INVOKESPECIAL: { @@ -958,10 +974,13 @@ * on "invokespecial" as well as section 4.8.2 (7th * bullet point) for the gory details. */ + /* TODO: Consider checking that invoke-special target + * method is private, or constructor since otherwise ART + * verifier will reject it. + */ CstMethodRef ref = (CstMethodRef) cst; if (ref.isInstanceInit() || - (ref.getDefiningClass().equals(method.getDefiningClass())) || - !method.getAccSuper()) { + (ref.getDefiningClass().equals(method.getDefiningClass()))) { return RegOps.INVOKE_DIRECT; } return RegOps.INVOKE_SUPER; @@ -972,6 +991,9 @@ case ByteOps.INVOKEINTERFACE: { return RegOps.INVOKE_INTERFACE; } + case ByteOps.INVOKEDYNAMIC: { + return RegOps.INVOKE_CUSTOM; + } case ByteOps.NEW: { return RegOps.NEW_INSTANCE; } @@ -1001,4 +1023,10 @@ throw new RuntimeException("shouldn't happen"); } + + private Insn makeInvokePolymorphicInsn(Rop rop, SourcePosition pos, RegisterSpecList sources, + TypeList catches, Constant cst) { + CstMethodRef cstMethodRef = (CstMethodRef) cst; + return new InvokePolymorphicInsn(rop, pos, sources, catches, cstMethodRef); + } } diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/Simulator.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/Simulator.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/Simulator.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/Simulator.java 2018-10-24 12:07:52.000000000 +0000 @@ -16,11 +16,15 @@ package com.android.dx.cf.code; +import com.android.dex.DexFormat; +import com.android.dx.dex.DexOptions; import com.android.dx.rop.code.LocalItem; import com.android.dx.rop.cst.Constant; +import com.android.dx.rop.cst.CstCallSiteRef; import com.android.dx.rop.cst.CstFieldRef; import com.android.dx.rop.cst.CstInteger; import com.android.dx.rop.cst.CstInterfaceMethodRef; +import com.android.dx.rop.cst.CstInvokeDynamic; import com.android.dx.rop.cst.CstMethodRef; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.type.Prototype; @@ -56,13 +60,17 @@ /** {@code non-null;} visitor instance to use */ private final SimVisitor visitor; + /** {@code non-null;} options for dex output */ + private final DexOptions dexOptions; + /** * Constructs an instance. * * @param machine {@code non-null;} machine to use when simulating * @param method {@code non-null;} method data to use + * @param dexOptions {@code non-null;} options for dex output */ - public Simulator(Machine machine, ConcreteMethod method) { + public Simulator(Machine machine, ConcreteMethod method, DexOptions dexOptions) { if (machine == null) { throw new NullPointerException("machine == null"); } @@ -75,6 +83,7 @@ this.code = method.getCode(); this.localVariables = method.getLocalVariables(); this.visitor = new SimVisitor(); + this.dexOptions = dexOptions; } /** @@ -105,7 +114,7 @@ * Simulates the effect of the instruction at the given offset, by * making appropriate calls on the given frame. * - * @param offset {@code >= 0;} offset of the instruction to simulate + * @param offset {@code offset >= 0;} offset of the instruction to simulate * @param frame {@code non-null;} frame to operate on * @return the length of the instruction, in bytes */ @@ -581,8 +590,9 @@ localType = local.getType(); if (localType.getBasicFrameType() != type.getBasicFrameType()) { - BaseMachine.throwLocalMismatch(type, localType); - return; + // wrong type, ignore local variable info + local = null; + localType = type; } } else { localType = type; @@ -647,33 +657,67 @@ machine.popArgs(frame, Type.OBJECT, fieldType); break; } - case ByteOps.INVOKEINTERFACE: { + case ByteOps.INVOKEINTERFACE: + case ByteOps.INVOKEVIRTUAL: + case ByteOps.INVOKESPECIAL: + case ByteOps.INVOKESTATIC: { /* * Convert the interface method ref into a normal - * method ref. + * method ref if necessary. */ - cst = ((CstInterfaceMethodRef) cst).toMethodRef(); - // and fall through... - } - case ByteOps.INVOKEVIRTUAL: - case ByteOps.INVOKESPECIAL: { + if (cst instanceof CstInterfaceMethodRef) { + if (opcode != ByteOps.INVOKEINTERFACE) { + if (!dexOptions.canUseDefaultInterfaceMethods()) { + throw new SimException( + "default or static interface method used without " + + "--min-sdk-version >= " + DexFormat.API_DEFAULT_INTERFACE_METHODS); + } + } + cst = ((CstInterfaceMethodRef) cst).toMethodRef(); + } + /* - * Get the instance prototype, and use it to direct - * the machine. + * Check whether invoke-polymorphic is required and supported. + */ + if (cst instanceof CstMethodRef) { + CstMethodRef methodRef = (CstMethodRef) cst; + if (methodRef.isSignaturePolymorphic()) { + if (!dexOptions.canUseInvokePolymorphic()) { + throw new SimException( + "signature-polymorphic method called without " + + "--min-sdk-version >= " + DexFormat.API_INVOKE_POLYMORPHIC); + } + if (opcode != ByteOps.INVOKEVIRTUAL) { + throw new SimException( + "Unsupported signature polymorphic invocation (" + + ByteOps.opName(opcode) + ")"); + } + } + } + + /* + * Get the instance or static prototype, and use it to + * direct the machine. */ + boolean staticMethod = (opcode == ByteOps.INVOKESTATIC); Prototype prototype = - ((CstMethodRef) cst).getPrototype(false); + ((CstMethodRef) cst).getPrototype(staticMethod); machine.popArgs(frame, prototype); break; } - case ByteOps.INVOKESTATIC: { - /* - * Get the static prototype, and use it to direct - * the machine. - */ - Prototype prototype = - ((CstMethodRef) cst).getPrototype(true); + case ByteOps.INVOKEDYNAMIC: { + if (!dexOptions.canUseInvokeCustom()) { + throw new SimException( + "invalid opcode " + Hex.u1(opcode) + + " (invokedynamic requires --min-sdk-version >= " + + DexFormat.API_INVOKE_POLYMORPHIC + ")"); + } + CstInvokeDynamic invokeDynamicRef = (CstInvokeDynamic) cst; + Prototype prototype = invokeDynamicRef.getPrototype(); machine.popArgs(frame, prototype); + // Change the constant to be associated with instruction to + // a call site reference. + cst = invokeDynamicRef.addReference(); break; } case ByteOps.MULTIANEWARRAY: { diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/ValueAwareMachine.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/ValueAwareMachine.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/code/ValueAwareMachine.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/code/ValueAwareMachine.java 2018-10-24 12:07:52.000000000 +0000 @@ -16,6 +16,7 @@ package com.android.dx.cf.code; +import com.android.dx.rop.cst.CstCallSiteRef; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.type.Prototype; import com.android.dx.rop.type.Type; @@ -163,6 +164,15 @@ if (type == Type.VOID) { clearResult(); } else { + setResult(type); + } + break; + } + case ByteOps.INVOKEDYNAMIC: { + Type type = ((CstCallSiteRef) getAuxCst()).getReturnType(); + if (type == Type.VOID) { + clearResult(); + } else { setResult(type); } break; diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java 2018-10-24 12:07:52.000000000 +0000 @@ -38,9 +38,12 @@ import com.android.dx.rop.cst.CstFloat; import com.android.dx.rop.cst.CstInteger; import com.android.dx.rop.cst.CstInterfaceMethodRef; +import com.android.dx.rop.cst.CstInvokeDynamic; import com.android.dx.rop.cst.CstLong; +import com.android.dx.rop.cst.CstMethodHandle; import com.android.dx.rop.cst.CstMethodRef; import com.android.dx.rop.cst.CstNat; +import com.android.dx.rop.cst.CstProtoRef; import com.android.dx.rop.cst.CstString; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.cst.StdConstantPool; @@ -217,13 +220,19 @@ break; } case CONSTANT_MethodHandle: { - throw new ParseException("MethodHandle not supported"); + lastCategory = 1; + at += 4; + break; } case CONSTANT_MethodType: { - throw new ParseException("MethodType not supported"); + lastCategory = 1; + at += 3; + break; } case CONSTANT_InvokeDynamic: { - throw new ParseException("InvokeDynamic not supported"); + lastCategory = 1; + at += 5; + break; } default: { throw new ParseException("unknown tag byte: " + Hex.u1(tag)); @@ -327,13 +336,53 @@ break; } case CONSTANT_MethodHandle: { - throw new ParseException("MethodHandle not supported"); + final int kind = bytes.getUnsignedByte(at + 1); + final int constantIndex = bytes.getUnsignedShort(at + 2); + final Constant ref; + switch (kind) { + case MethodHandleKind.REF_getField: + case MethodHandleKind.REF_getStatic: + case MethodHandleKind.REF_putField: + case MethodHandleKind.REF_putStatic: + ref = (CstFieldRef) parse0(constantIndex, wasUtf8); + break; + case MethodHandleKind.REF_invokeVirtual: + case MethodHandleKind.REF_newInvokeSpecial: + ref = (CstMethodRef) parse0(constantIndex, wasUtf8); + break; + case MethodHandleKind.REF_invokeStatic: + case MethodHandleKind.REF_invokeSpecial: + ref = parse0(constantIndex, wasUtf8); + if (!(ref instanceof CstMethodRef + || ref instanceof CstInterfaceMethodRef)) { + throw new ParseException( + "Unsupported ref constant type for MethodHandle " + + ref.getClass()); + } + break; + case MethodHandleKind.REF_invokeInterface: + ref = (CstInterfaceMethodRef) parse0(constantIndex, wasUtf8); + break; + default: + throw new ParseException("Unsupported MethodHandle kind: " + kind); + } + + final int methodHandleType = getMethodHandleTypeForKind(kind); + cst = CstMethodHandle.make(methodHandleType, ref); + break; } case CONSTANT_MethodType: { - throw new ParseException("MethodType not supported"); + int descriptorIndex = bytes.getUnsignedShort(at + 1); + CstString descriptor = (CstString) parse0(descriptorIndex, wasUtf8); + cst = CstProtoRef.make(descriptor); + break; } case CONSTANT_InvokeDynamic: { - throw new ParseException("InvokeDynamic not supported"); + int bootstrapMethodIndex = bytes.getUnsignedShort(at + 1); + int natIndex = bytes.getUnsignedShort(at + 3); + CstNat nat = (CstNat) parse0(natIndex, wasUtf8); + cst = CstInvokeDynamic.make(bootstrapMethodIndex, nat); + break; } default: { throw new ParseException("unknown tag byte: " + Hex.u1(tag)); @@ -374,4 +423,28 @@ throw new ParseException(ex); } } + + private static int getMethodHandleTypeForKind(int kind) { + switch (kind) { + case MethodHandleKind.REF_getField: + return CstMethodHandle.METHOD_HANDLE_TYPE_INSTANCE_GET; + case MethodHandleKind.REF_getStatic: + return CstMethodHandle.METHOD_HANDLE_TYPE_STATIC_GET; + case MethodHandleKind.REF_putField: + return CstMethodHandle.METHOD_HANDLE_TYPE_INSTANCE_PUT; + case MethodHandleKind.REF_putStatic: + return CstMethodHandle.METHOD_HANDLE_TYPE_STATIC_PUT; + case MethodHandleKind.REF_invokeVirtual: + return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_INSTANCE; + case MethodHandleKind.REF_invokeStatic: + return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_STATIC; + case MethodHandleKind.REF_invokeSpecial: + return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_DIRECT; + case MethodHandleKind.REF_newInvokeSpecial: + return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_CONSTRUCTOR; + case MethodHandleKind.REF_invokeInterface: + return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_INTERFACE; + } + throw new IllegalArgumentException("invalid kind: " + kind); + } } diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/cst/MethodHandleKind.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/cst/MethodHandleKind.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/cst/MethodHandleKind.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/cst/MethodHandleKind.java 2018-10-24 12:07:52.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 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.dx.cf.cst; + +/** + * Method Handle kinds for {@code CONSTANT_MethodHandle_info} constants. + */ +public interface MethodHandleKind { + /** A method handle that gets an instance field. */ + int REF_getField = 1; + + /** A method handle that gets a static field. */ + int REF_getStatic = 2; + + /** A method handle that sets an instance field. */ + int REF_putField = 3; + + /** A method handle that sets a static field. */ + int REF_putStatic = 4; + + /** A method handle for {@code invokevirtual}. */ + int REF_invokeVirtual = 5; + + /** A method handle for {@code invokestatic}. */ + int REF_invokeStatic = 6; + + /** A method handle for {@code invokespecial}. */ + int REF_invokeSpecial = 7; + + /** A method handle for invoking a constructor. */ + int REF_newInvokeSpecial = 8; + + /** A method handle for {@code invokeinterface}. */ + int REF_invokeInterface = 9; +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/direct/DirectClassFile.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/direct/DirectClassFile.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/direct/DirectClassFile.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/direct/DirectClassFile.java 2018-10-24 12:07:52.000000000 +0000 @@ -16,7 +16,9 @@ package com.android.dx.cf.direct; +import com.android.dx.cf.attrib.AttBootstrapMethods; import com.android.dx.cf.attrib.AttSourceFile; +import com.android.dx.cf.code.BootstrapMethodsList; import com.android.dx.cf.cst.ConstantPoolParser; import com.android.dx.cf.iface.Attribute; import com.android.dx.cf.iface.AttributeList; @@ -69,7 +71,7 @@ * * Note: if you change this, please change "java.class.version" in System.java. */ - private static final int CLASS_FILE_MAX_MAJOR_VERSION = 51; + private static final int CLASS_FILE_MAX_MAJOR_VERSION = 52; /** maximum {@code .class} file minor version */ private static final int CLASS_FILE_MAX_MINOR_VERSION = 0; @@ -313,6 +315,18 @@ } /** {@inheritDoc} */ + @Override + public BootstrapMethodsList getBootstrapMethods() { + AttBootstrapMethods bootstrapMethodsAttribute = + (AttBootstrapMethods) getAttributes().findFirst(AttBootstrapMethods.ATTRIBUTE_NAME); + if (bootstrapMethodsAttribute != null) { + return bootstrapMethodsAttribute.getBootstrapMethods(); + } else { + return BootstrapMethodsList.EMPTY; + } + } + + /** {@inheritDoc} */ public CstString getSourceFile() { AttributeList attribs = getAttributes(); Attribute attSf = attribs.findFirst(AttSourceFile.ATTRIBUTE_NAME); diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java 2018-10-24 12:07:52.000000000 +0000 @@ -17,6 +17,7 @@ package com.android.dx.cf.direct; import com.android.dx.cf.attrib.AttAnnotationDefault; +import com.android.dx.cf.attrib.AttBootstrapMethods; import com.android.dx.cf.attrib.AttCode; import com.android.dx.cf.attrib.AttConstantValue; import com.android.dx.cf.attrib.AttDeprecated; @@ -31,9 +32,12 @@ import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations; import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations; import com.android.dx.cf.attrib.AttSignature; +import com.android.dx.cf.attrib.AttSourceDebugExtension; import com.android.dx.cf.attrib.AttSourceFile; import com.android.dx.cf.attrib.AttSynthetic; import com.android.dx.cf.attrib.InnerClassList; +import com.android.dx.cf.code.BootstrapMethodArgumentsList; +import com.android.dx.cf.code.BootstrapMethodsList; import com.android.dx.cf.code.ByteCatchList; import com.android.dx.cf.code.BytecodeArray; import com.android.dx.cf.code.LineNumberList; @@ -48,6 +52,7 @@ import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.cst.Constant; import com.android.dx.rop.cst.ConstantPool; +import com.android.dx.rop.cst.CstMethodHandle; import com.android.dx.rop.cst.CstNat; import com.android.dx.rop.cst.CstString; import com.android.dx.rop.cst.CstType; @@ -80,6 +85,9 @@ int offset, int length, ParseObserver observer) { switch (context) { case CTX_CLASS: { + if (name == AttBootstrapMethods.ATTRIBUTE_NAME) { + return bootstrapMethods(cf, offset, length, observer); + } if (name == AttDeprecated.ATTRIBUTE_NAME) { return deprecated(cf, offset, length, observer); } @@ -103,6 +111,9 @@ if (name == AttSignature.ATTRIBUTE_NAME) { return signature(cf, offset, length, observer); } + if (name == AttSourceDebugExtension.ATTRIBUTE_NAME) { + return sourceDebugExtension(cf, offset, length, observer); + } if (name == AttSourceFile.ATTRIBUTE_NAME) { return sourceFile(cf, offset, length, observer); } @@ -205,6 +216,31 @@ } /** + * Parses a {@code BootstrapMethods} attribute. + */ + private Attribute bootstrapMethods(DirectClassFile cf, int offset, int length, + ParseObserver observer) { + if (length < 2) { + return throwSeverelyTruncated(); + } + + ByteArray bytes = cf.getBytes(); + int numMethods = bytes.getUnsignedShort(offset); + if (observer != null) { + observer.parsed(bytes, offset, 2, + "num_boostrap_methods: " + Hex.u2(numMethods)); + } + + offset += 2; + length -= 2; + + BootstrapMethodsList methods = parseBootstrapMethods(bytes, cf.getConstantPool(), + cf.getThisClass(), numMethods, + offset, length, observer); + return new AttBootstrapMethods(methods); + } + + /** * Parses a {@code Code} attribute. */ private Attribute code(DirectClassFile cf, int offset, int length, @@ -692,6 +728,23 @@ } /** + * Parses a {@code SourceDebugExtesion} attribute. + */ + private Attribute sourceDebugExtension(DirectClassFile cf, int offset, int length, + ParseObserver observer) { + ByteArray bytes = cf.getBytes().slice(offset, offset + length); + CstString smapString = new CstString(bytes); + Attribute result = new AttSourceDebugExtension(smapString); + + if (observer != null) { + String decoded = smapString.getString(); + observer.parsed(bytes, offset, length, "sourceDebugExtension: " + decoded); + } + + return result; + } + + /** * Parses a {@code SourceFile} attribute. */ private Attribute sourceFile(DirectClassFile cf, int offset, int length, @@ -759,4 +812,50 @@ throw new ParseException("bad attribute length; expected length " + Hex.u4(expected)); } + + private BootstrapMethodsList parseBootstrapMethods(ByteArray bytes, ConstantPool constantPool, + CstType declaringClass, int numMethods, int offset, int length, ParseObserver observer) + throws ParseException { + BootstrapMethodsList methods = new BootstrapMethodsList(numMethods); + for (int methodIndex = 0; methodIndex < numMethods; ++methodIndex) { + if (length < 4) { + throwTruncated(); + } + + int methodRef = bytes.getUnsignedShort(offset); + int numArguments = bytes.getUnsignedShort(offset + 2); + + if (observer != null) { + observer.parsed(bytes, offset, 2, "bootstrap_method_ref: " + Hex.u2(methodRef)); + observer.parsed(bytes, offset + 2, 2, + "num_bootstrap_arguments: " + Hex.u2(numArguments)); + } + + offset += 4; + length -= 4; + if (length < numArguments * 2) { + throwTruncated(); + } + + BootstrapMethodArgumentsList arguments = new BootstrapMethodArgumentsList(numArguments); + for (int argIndex = 0; argIndex < numArguments; ++argIndex, offset += 2, length -= 2) { + int argumentRef = bytes.getUnsignedShort(offset); + if (observer != null) { + observer.parsed(bytes, offset, 2, + "bootstrap_arguments[" + argIndex + "]" + Hex.u2(argumentRef)); + } + arguments.set(argIndex, constantPool.get(argumentRef)); + } + arguments.setImmutable(); + Constant cstMethodRef = constantPool.get(methodRef); + methods.set(methodIndex, declaringClass, (CstMethodHandle) cstMethodRef, arguments); + } + methods.setImmutable(); + + if (length != 0) { + throwBadLength(length); + } + + return methods; + } } diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/iface/ClassFile.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/iface/ClassFile.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/cf/iface/ClassFile.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/cf/iface/ClassFile.java 2018-10-24 12:07:52.000000000 +0000 @@ -16,6 +16,7 @@ package com.android.dx.cf.iface; +import com.android.dx.cf.code.BootstrapMethodsList; import com.android.dx.rop.cst.ConstantPool; import com.android.dx.rop.cst.CstString; import com.android.dx.rop.cst.CstType; @@ -113,6 +114,12 @@ public AttributeList getAttributes(); /** + * Gets the bootstrap method {@code attributes}. + * @return {@code non-null;} the list of bootstrap methods + */ + public BootstrapMethodsList getBootstrapMethods(); + + /** * Gets the name out of the {@code SourceFile} attribute of this * file, if any. This is a convenient shorthand for scrounging around * the class's attributes. diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dexer/DxContext.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dexer/DxContext.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dexer/DxContext.java 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dexer/DxContext.java 2018-10-24 12:07:52.000000000 +0000 @@ -0,0 +1,35 @@ +package com.android.dx.command.dexer; + +import com.android.dx.dex.cf.CodeStatistics; +import com.android.dx.dex.cf.OptimizerOptions; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * State used by a single invocation of {@link Main}. + */ +public class DxContext { + public final CodeStatistics codeStatistics = new CodeStatistics(); + public final OptimizerOptions optimizerOptions = new OptimizerOptions(); + public final PrintStream out; + public final PrintStream err; + + @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") + final PrintStream noop = new PrintStream(new OutputStream() { + @Override + public void write(int b) throws IOException { + // noop; + } + }); + + public DxContext(OutputStream out, OutputStream err) { + this.out = new PrintStream(out); + this.err = new PrintStream(err); + } + + public DxContext() { + this(System.out, System.err); + } +} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dexer/Main.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dexer/Main.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dexer/Main.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dexer/Main.java 2018-10-24 12:07:52.000000000 +0000 @@ -27,12 +27,10 @@ import com.android.dx.cf.direct.DirectClassFile; import com.android.dx.cf.direct.StdAttributeFactory; import com.android.dx.cf.iface.ParseException; -import com.android.dx.command.DxConsole; import com.android.dx.command.UsageException; import com.android.dx.dex.DexOptions; import com.android.dx.dex.cf.CfOptions; import com.android.dx.dex.cf.CfTranslator; -import com.android.dx.dex.cf.CodeStatistics; import com.android.dx.dex.code.PositionList; import com.android.dx.dex.file.ClassDefItem; import com.android.dx.dex.file.DexFile; @@ -42,8 +40,12 @@ import com.android.dx.rop.annotation.Annotation; import com.android.dx.rop.annotation.Annotations; import com.android.dx.rop.annotation.AnnotationsList; +import com.android.dx.rop.code.RegisterSpec; import com.android.dx.rop.cst.CstNat; import com.android.dx.rop.cst.CstString; +import com.android.dx.rop.cst.CstType; +import com.android.dx.rop.type.Prototype; +import com.android.dx.rop.type.Type; import java.io.BufferedReader; import java.io.ByteArrayInputStream; @@ -70,7 +72,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -84,6 +85,8 @@ */ public class Main { + public static final int CONCURRENCY_LEVEL = 4; + /** * File extension of a {@code .dex} file. */ @@ -164,74 +167,72 @@ private static final int MAX_FIELD_ADDED_DURING_DEX_CREATION = 9; /** number of errors during processing */ - private static AtomicInteger errors = new AtomicInteger(0); + private AtomicInteger errors = new AtomicInteger(0); /** {@code non-null;} parsed command-line arguments */ - private static Arguments args; + private Arguments args; /** {@code non-null;} output file in-progress */ - private static DexFile outputDex; + private DexFile outputDex; /** * {@code null-ok;} map of resources to include in the output, or * {@code null} if resources are being ignored */ - private static TreeMap outputResources; + private TreeMap outputResources; /** Library .dex files to merge into the output .dex. */ - private static final List libraryDexBuffers = new ArrayList(); + private final List libraryDexBuffers = new ArrayList(); /** Thread pool object used for multi-thread class translation. */ - private static ExecutorService classTranslatorPool; + private ExecutorService classTranslatorPool; /** Single thread executor, for collecting results of parallel translation, * and adding classes to dex file in original input file order. */ - private static ExecutorService classDefItemConsumer; + private ExecutorService classDefItemConsumer; /** Futures for {@code classDefItemConsumer} tasks. */ - private static List> addToDexFutures = + private List> addToDexFutures = new ArrayList>(); /** Thread pool object used for multi-thread dex conversion (to byte array). * Used in combination with multi-dex support, to allow outputing * a completed dex file, in parallel with continuing processing. */ - private static ExecutorService dexOutPool; + private ExecutorService dexOutPool; /** Futures for {@code dexOutPool} task. */ - private static List> dexOutputFutures = - new ArrayList>(); + private List> dexOutputFutures = new ArrayList>(); /** Lock object used to to coordinate dex file rotation, and * multi-threaded translation. */ - private static Object dexRotationLock = new Object(); + private Object dexRotationLock = new Object(); /** Record the number if method indices "reserved" for files * committed to translation in the context of the current dex * file, but not yet added. */ - private static int maxMethodIdsInProcess = 0; + private int maxMethodIdsInProcess = 0; /** Record the number if field indices "reserved" for files * committed to translation in the context of the current dex * file, but not yet added. */ - private static int maxFieldIdsInProcess = 0; + private int maxFieldIdsInProcess = 0; /** true if any files are successfully processed */ - private static volatile boolean anyFilesProcessed; + private volatile boolean anyFilesProcessed; /** class files older than this must be defined in the target dex file. */ - private static long minimumFileAge = 0; + private long minimumFileAge = 0; - private static Set classesInMainDex = null; + private Set classesInMainDex = null; - private static List dexOutputArrays = new ArrayList(); + private List dexOutputArrays = new ArrayList(); - private static OutputStreamWriter humanOutWriter = null; + private OutputStreamWriter humanOutWriter = null; - /** - * This class is uninstantiable. - */ - private Main() { - // This space intentionally left blank. + private final DxContext context; + + public Main(DxContext context) { + this.context = context; } /** @@ -239,21 +240,34 @@ * @param argArray the command line arguments */ public static void main(String[] argArray) throws IOException { - Arguments arguments = new Arguments(); + DxContext context = new DxContext(); + Arguments arguments = new Arguments(context); arguments.parse(argArray); - int result = run(arguments); + int result = new Main(context).runDx(arguments); + if (result != 0) { System.exit(result); } } + public static void clearInternTables() { + Prototype.clearInternTable(); + RegisterSpec.clearInternTable(); + CstType.clearInternTable(); + Type.clearInternTable(); + } + /** * Run and return a result code. * @param arguments the data + parameters for the conversion - * @return 0 if success > 0 otherwise. + * @return 0 if success > 0 otherwise. */ public static int run(Arguments arguments) throws IOException { + return new Main(new DxContext()).runDx(arguments); + } + + public int runDx(Arguments arguments) throws IOException { // Reset the error count to start fresh. errors.set(0); @@ -281,24 +295,12 @@ } } - /** - * {@code non-null;} Error message for too many method/field/type ids. - */ - public static String getTooManyIdsErrorMessage() { - if (args.multiDex) { - return "The list of classes given in " + Arguments.MAIN_DEX_LIST_OPTION + - " is too big and does not fit in the main dex."; - } else { - return "You may try using " + Arguments.MULTI_DEX_OPTION + " option."; - } - } - - private static int runMonoDex() throws IOException { + private int runMonoDex() throws IOException { File incrementalOutFile = null; if (args.incremental) { if (args.outName == null) { - System.err.println( + context.err.println( "error: no incremental output name specified"); return -1; } @@ -352,7 +354,7 @@ return 0; } - private static int runMultiDex() throws IOException { + private int runMultiDex() throws IOException { assert !args.incremental; @@ -458,7 +460,7 @@ * @return the bytes of the merged dex file, or null if both the update * and the base dex do not exist. */ - private static byte[] mergeIncremental(byte[] update, File base) throws IOException { + private byte[] mergeIncremental(byte[] update, File base) throws IOException { Dex dexA = null; Dex dexB = null; @@ -478,7 +480,7 @@ } else if (dexB == null) { result = dexA; } else { - result = new DexMerger(new Dex[] {dexA, dexB}, CollisionPolicy.KEEP_FIRST).merge(); + result = new DexMerger(new Dex[] {dexA, dexB}, CollisionPolicy.KEEP_FIRST, context).merge(); } ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); @@ -490,7 +492,7 @@ * Merges the dex files in library jars. If multiple dex files define the * same type, this fails with an exception. */ - private static byte[] mergeLibraryDexBuffers(byte[] outArray) throws IOException { + private byte[] mergeLibraryDexBuffers(byte[] outArray) throws IOException { ArrayList dexes = new ArrayList(); if (outArray != null) { dexes.add(new Dex(outArray)); @@ -501,7 +503,7 @@ if (dexes.isEmpty()) { return null; } - Dex merged = new DexMerger(dexes.toArray(new Dex[dexes.size()]), CollisionPolicy.FAIL).merge(); + Dex merged = new DexMerger(dexes.toArray(new Dex[dexes.size()]), CollisionPolicy.FAIL, context).merge(); return merged.getBytes(); } @@ -511,7 +513,7 @@ * * @return whether processing was successful */ - private static boolean processAllFiles() { + private boolean processAllFiles() { createDexFile(); if (args.jarOutput) { @@ -596,10 +598,10 @@ int count = errors.incrementAndGet(); if (count < 10) { if (args.debug) { - DxConsole.err.println("Uncaught translation error:"); - ex.getCause().printStackTrace(DxConsole.err); + context.err.println("Uncaught translation error:"); + ex.getCause().printStackTrace(context.err); } else { - DxConsole.err.println("Uncaught translation error: " + ex.getCause()); + context.err.println("Uncaught translation error: " + ex.getCause()); } } else { throw new InterruptedException("Too many errors"); @@ -614,13 +616,13 @@ } catch (Exception e) { classTranslatorPool.shutdownNow(); classDefItemConsumer.shutdownNow(); - e.printStackTrace(System.out); + e.printStackTrace(context.out); throw new RuntimeException("Unexpected exception in translator thread.", e); } int errorNum = errors.get(); if (errorNum != 0) { - DxConsole.err.println(errorNum + " error" + + context.err.println(errorNum + " error" + ((errorNum == 1) ? "" : "s") + "; aborting"); return false; } @@ -630,18 +632,18 @@ } if (!(anyFilesProcessed || args.emptyOk)) { - DxConsole.err.println("no classfiles specified"); + context.err.println("no classfiles specified"); return false; } if (args.optimize && args.statistics) { - CodeStatistics.dumpStatistics(DxConsole.out); + context.codeStatistics.dumpStatistics(context.out); } return true; } - private static void createDexFile() { + private void createDexFile() { outputDex = new DexFile(args.dexOptions); if (args.dumpWidth != 0) { @@ -649,7 +651,7 @@ } } - private static void rotateDexFile() { + private void rotateDexFile() { if (outputDex != null) { if (dexOutPool != null) { dexOutputFutures.add(dexOutPool.submit(new DexWriter(outputDex))); @@ -669,7 +671,7 @@ * containing class files. * @param filter {@code non-null;} A filter for excluding files. */ - private static void processOne(String pathname, FileNameFilter filter) { + private void processOne(String pathname, FileNameFilter filter) { ClassPathOpener opener; opener = new ClassPathOpener(pathname, true, filter, new FileBytesConsumer()); @@ -679,7 +681,7 @@ } } - private static void updateStatus(boolean res) { + private void updateStatus(boolean res) { anyFilesProcessed |= res; } @@ -691,7 +693,7 @@ * @param bytes {@code non-null;} contents of the file * @return whether processing was successful */ - private static boolean processFileBytes(String name, long lastModified, byte[] bytes) { + private boolean processFileBytes(String name, long lastModified, byte[] bytes) { boolean isClass = name.endsWith(".class"); boolean isClassesDex = name.equals(DexFormat.DEX_IN_JAR_NAME); @@ -699,13 +701,13 @@ if (!isClass && !isClassesDex && !keepResources) { if (args.verbose) { - DxConsole.out.println("ignored resource " + name); + context.out.println("ignored resource " + name); } return false; } if (args.verbose) { - DxConsole.out.println("processing " + name + "..."); + context.out.println("processing " + name + "..."); } String fixedName = fixPath(name); @@ -745,7 +747,7 @@ * @param bytes {@code non-null;} contents of the file * @return whether processing was successful */ - private static boolean processClass(String name, byte[] bytes) { + private boolean processClass(String name, byte[] bytes) { if (! args.coreLibrary) { checkClassName(name); } @@ -764,7 +766,7 @@ } - private static DirectClassFile parseClass(String name, byte[] bytes) { + private DirectClassFile parseClass(String name, byte[] bytes) { DirectClassFile cf = new DirectClassFile(bytes, name, args.cfOptions.strictNameCheck); @@ -773,23 +775,23 @@ return cf; } - private static ClassDefItem translateClass(byte[] bytes, DirectClassFile cf) { + private ClassDefItem translateClass(byte[] bytes, DirectClassFile cf) { try { - return CfTranslator.translate(cf, bytes, args.cfOptions, + return CfTranslator.translate(context, cf, bytes, args.cfOptions, args.dexOptions, outputDex); } catch (ParseException ex) { - DxConsole.err.println("\ntrouble processing:"); + context.err.println("\ntrouble processing:"); if (args.debug) { - ex.printStackTrace(DxConsole.err); + ex.printStackTrace(context.err); } else { - ex.printContext(DxConsole.err); + ex.printContext(context.err); } } errors.incrementAndGet(); return null; } - private static boolean addClassToDex(ClassDefItem clazz) { + private boolean addClassToDex(ClassDefItem clazz) { synchronized (outputDex) { outputDex.add(clazz); } @@ -804,7 +806,7 @@ * @param name {@code non-null;} the fully-qualified internal-form * class name */ - private static void checkClassName(String name) { + private void checkClassName(String name) { boolean bogus = false; if (name.startsWith("java/")) { @@ -830,7 +832,7 @@ * working. Try to help them understand what's happening. */ - DxConsole.err.println("\ntrouble processing \"" + name + "\":\n\n" + + context.err.println("\ntrouble processing \"" + name + "\":\n\n" + IN_RE_CORE_CLASSES); errors.incrementAndGet(); throw new StopProcessing(); @@ -843,7 +845,7 @@ * @return {@code null-ok;} the converted {@code byte[]} or {@code null} * if there was a problem */ - private static byte[] writeDex(DexFile outputDex) { + private byte[] writeDex(DexFile outputDex) { byte[] outArray = null; try { @@ -865,7 +867,7 @@ } if (args.statistics) { - DxConsole.out.println(outputDex.getStatistics().toHuman()); + context.out.println(outputDex.getStatistics().toHuman()); } } finally { if (humanOutWriter != null) { @@ -874,10 +876,10 @@ } } catch (Exception ex) { if (args.debug) { - DxConsole.err.println("\ntrouble writing output:"); - ex.printStackTrace(DxConsole.err); + context.err.println("\ntrouble writing output:"); + ex.printStackTrace(context.err); } else { - DxConsole.err.println("\ntrouble writing output: " + + context.err.println("\ntrouble writing output: " + ex.getMessage()); } return null; @@ -891,7 +893,7 @@ * @param fileName {@code non-null;} name of the file * @return whether the creation was successful */ - private static boolean createJar(String fileName) { + private boolean createJar(String fileName) { /* * Make or modify the manifest (as appropriate), put the dex * array into the resources map, and then process the entire @@ -912,7 +914,7 @@ int length = contents.length; if (args.verbose) { - DxConsole.out.println("writing " + name + "; size " + length + "..."); + context.out.println("writing " + name + "; size " + length + "..."); } entry.setSize(length); @@ -927,10 +929,10 @@ } } catch (Exception ex) { if (args.debug) { - DxConsole.err.println("\ntrouble writing output:"); - ex.printStackTrace(DxConsole.err); + context.err.println("\ntrouble writing output:"); + ex.printStackTrace(context.err); } else { - DxConsole.err.println("\ntrouble writing output: " + + context.err.println("\ntrouble writing output: " + ex.getMessage()); } return false; @@ -945,7 +947,7 @@ * * @return {@code non-null;} the manifest */ - private static Manifest makeManifest() throws IOException { + private Manifest makeManifest() throws IOException { byte[] manifestBytes = outputResources.get(MANIFEST_NAME); Manifest manifest; Attributes attribs; @@ -981,10 +983,10 @@ * @param name {@code non-null;} the file name * @return {@code non-null;} the opened file */ - private static OutputStream openOutput(String name) throws IOException { + private OutputStream openOutput(String name) throws IOException { if (name.equals("-") || name.startsWith("-.")) { - return System.out; + return context.out; } return new FileOutputStream(name); @@ -998,14 +1000,14 @@ * * @param stream {@code null-ok;} what to close */ - private static void closeOutput(OutputStream stream) throws IOException { + private void closeOutput(OutputStream stream) throws IOException { if (stream == null) { return; } stream.flush(); - if (stream != System.out) { + if (stream != context.out) { stream.close(); } } @@ -1055,13 +1057,13 @@ * method(s) * @param out {@code non-null;} where to dump to */ - private static void dumpMethod(DexFile dex, String fqName, + private void dumpMethod(DexFile dex, String fqName, OutputStreamWriter out) { boolean wildcard = fqName.endsWith("*"); int lastDot = fqName.lastIndexOf('.'); if ((lastDot <= 0) || (lastDot == (fqName.length() - 1))) { - DxConsole.err.println("bogus fully-qualified method name: " + + context.err.println("bogus fully-qualified method name: " + fqName); return; } @@ -1071,7 +1073,7 @@ ClassDefItem clazz = dex.getClassOrNull(className); if (clazz == null) { - DxConsole.err.println("no such class: " + className); + context.err.println("no such class: " + className); return; } @@ -1097,7 +1099,7 @@ } if (meths.size() == 0) { - DxConsole.err.println("no such method: " + fqName); + context.err.println("no such method: " + fqName); return; } @@ -1160,7 +1162,7 @@ /** * A quick and accurate filter for when file path can be trusted. */ - private static class MainDexListFilter implements FileNameFilter { + private class MainDexListFilter implements FileNameFilter { @Override public boolean accept(String fullPath) { @@ -1176,7 +1178,7 @@ /** * A best effort conservative filter for when file path can not be trusted. */ - private static class BestEffortMainDexListFilter implements FileNameFilter { + private class BestEffortMainDexListFilter implements FileNameFilter { Map> map = new HashMap>(); @@ -1212,7 +1214,7 @@ } } - private static String getSimpleName(String path) { + private String getSimpleName(String path) { int index = path.lastIndexOf('/'); if (index >= 0) { return path.substring(index + 1); @@ -1246,6 +1248,8 @@ private static final String INPUT_LIST_OPTION = "--input-list"; + public final DxContext context; + /** whether to run in debug mode */ public boolean debug = false; @@ -1294,6 +1298,9 @@ */ public boolean keepClassesInJar = false; + /** what API level to target */ + public int minSdkVersion = DexFormat.API_NO_EXTENDED_OPCODES; + /** how much source position info to preserve */ public int positionInfo = PositionList.LINES; @@ -1342,10 +1349,21 @@ * mainDexListFile is specified and non empty. */ public boolean minimalMainDex = false; + public int maxNumberOfIdxPerDex = DexFormat.MAX_MEMBER_IDX + 1; + /** Optional list containing inputs read in from a file. */ private List inputList = null; - private int maxNumberOfIdxPerDex = DexFormat.MAX_MEMBER_IDX + 1; + private boolean outputIsDirectory = false; + private boolean outputIsDirectDex = false; + + public Arguments(DxContext context) { + this.context = context; + } + + public Arguments() { + this(new DxContext()); + } private static class ArgumentsParser { @@ -1448,16 +1466,7 @@ } } - /** - * Parses the given command-line arguments. - * - * @param args {@code non-null;} the arguments - */ - public void parse(String[] args) { - ArgumentsParser parser = new ArgumentsParser(args); - - boolean outputIsDirectory = false; - boolean outputIsDirectDex = false; + private void parseFlags(ArgumentsParser parser) { while(parser.getNext()) { if (parser.isArg("--debug")) { @@ -1480,7 +1489,7 @@ statistics = true; } else if (parser.isArg("--optimize-list=")) { if (dontOptimizeListFile != null) { - System.err.println("--optimize-list and " + context.err.println("--optimize-list and " + "--no-optimize-list are incompatible."); throw new UsageException(); } @@ -1488,7 +1497,7 @@ optimizeListFile = parser.getLastValue(); } else if (parser.isArg("--no-optimize-list=")) { if (dontOptimizeListFile != null) { - System.err.println("--optimize-list and " + context.err.println("--optimize-list and " + "--no-optimize-list are incompatible."); throw new UsageException(); } @@ -1508,8 +1517,8 @@ jarOutput = false; outputIsDirectDex = true; } else { - System.err.println("unknown output extension: " + - outName); + context.err.println("unknown output extension: " + + outName); throw new UsageException(); } } else if (parser.isArg("--dump-to=")) { @@ -1528,8 +1537,8 @@ } else if (pstr == "lines") { positionInfo = PositionList.LINES; } else { - System.err.println("unknown positions option: " + - pstr); + context.err.println("unknown positions option: " + + pstr); throw new UsageException(); } } else if (parser.isArg("--no-locals")) { @@ -1550,20 +1559,46 @@ maxNumberOfIdxPerDex = Integer.parseInt(parser.getLastValue()); } else if(parser.isArg(INPUT_LIST_OPTION + "=")) { File inputListFile = new File(parser.getLastValue()); - try{ + try { inputList = new ArrayList(); readPathsFromFile(inputListFile.getAbsolutePath(), inputList); - } catch(IOException e) { - System.err.println( + } catch (IOException e) { + context.err.println( "Unable to read input list file: " + inputListFile.getName()); // problem reading the file so we should halt execution throw new UsageException(); } + } else if (parser.isArg("--min-sdk-version=")) { + String arg = parser.getLastValue(); + int value; + try { + value = Integer.parseInt(arg); + } catch (NumberFormatException ex) { + value = -1; + } + if (value < 1) { + System.err.println("improper min-sdk-version option: " + arg); + throw new UsageException(); + } + minSdkVersion = value; } else { - System.err.println("unknown option: " + parser.getCurrent()); + context.err.println("unknown option: " + parser.getCurrent()); throw new UsageException(); } } + } + + + /** + * Parses all command-line arguments and updates the state of the {@code Arguments} object + * accordingly. + * + * @param args {@code non-null;} the arguments + */ + private void parse(String[] args) { + ArgumentsParser parser = new ArgumentsParser(args); + + parseFlags(parser); fileNames = parser.getRemaining(); if(inputList != null && !inputList.isEmpty()) { @@ -1574,11 +1609,11 @@ if (fileNames.length == 0) { if (!emptyOk) { - System.err.println("no input files specified"); + context.err.println("no input files specified"); throw new UsageException(); } } else if (emptyOk) { - System.out.println("ignoring input files"); + context.out.println("ignoring input files"); } if ((humanOutName == null) && (methodToDump != null)) { @@ -1586,25 +1621,25 @@ } if (mainDexListFile != null && !multiDex) { - System.err.println(MAIN_DEX_LIST_OPTION + " is only supported in combination with " + context.err.println(MAIN_DEX_LIST_OPTION + " is only supported in combination with " + MULTI_DEX_OPTION); throw new UsageException(); } if (minimalMainDex && (mainDexListFile == null || !multiDex)) { - System.err.println(MINIMAL_MAIN_DEX_OPTION + " is only supported in combination with " + context.err.println(MINIMAL_MAIN_DEX_OPTION + " is only supported in combination with " + MULTI_DEX_OPTION + " and " + MAIN_DEX_LIST_OPTION); throw new UsageException(); } if (multiDex && incremental) { - System.err.println(INCREMENTAL_OPTION + " is not supported with " + context.err.println(INCREMENTAL_OPTION + " is not supported with " + MULTI_DEX_OPTION); throw new UsageException(); } if (multiDex && outputIsDirectDex) { - System.err.println("Unsupported output \"" + outName +"\". " + MULTI_DEX_OPTION + + context.err.println("Unsupported output \"" + outName +"\". " + MULTI_DEX_OPTION + " supports only archive or directory output"); throw new UsageException(); } @@ -1617,10 +1652,19 @@ } /** - * Copies relevent arguments over into CfOptions and - * DexOptions instances. + * Parses only command-line flags and updates the state of the {@code Arguments} object + * accordingly. + * + * @param flags {@code non-null;} the flags + */ + public void parseFlags(String[] flags) { + parseFlags(new ArgumentsParser(flags)); + } + + /** + * Copies relevant arguments over into CfOptions and DexOptions instances. */ - private void makeOptionsObjects() { + public void makeOptionsObjects() { cfOptions = new CfOptions(); cfOptions.positionInfo = positionInfo; cfOptions.localInfo = localInfo; @@ -1631,12 +1675,13 @@ cfOptions.statistics = statistics; if (warnings) { - cfOptions.warn = DxConsole.err; + cfOptions.warn = context.err; } else { - cfOptions.warn = DxConsole.noop; + cfOptions.warn = context.noop; } dexOptions = new DexOptions(); + dexOptions.minSdkVersion = minSdkVersion; dexOptions.forceJumbo = forceJumbo; } } @@ -1645,12 +1690,12 @@ * Callback class for processing input file bytes, produced by the * ClassPathOpener. */ - private static class FileBytesConsumer implements ClassPathOpener.Consumer { + private class FileBytesConsumer implements ClassPathOpener.Consumer { @Override public boolean processFileBytes(String name, long lastModified, byte[] bytes) { - return Main.processFileBytes(name, lastModified, bytes); + return Main.this.processFileBytes(name, lastModified, bytes); } @Override @@ -1658,20 +1703,20 @@ if (ex instanceof StopProcessing) { throw (StopProcessing) ex; } else if (ex instanceof SimException) { - DxConsole.err.println("\nEXCEPTION FROM SIMULATION:"); - DxConsole.err.println(ex.getMessage() + "\n"); - DxConsole.err.println(((SimException) ex).getContext()); + context.err.println("\nEXCEPTION FROM SIMULATION:"); + context.err.println(ex.getMessage() + "\n"); + context.err.println(((SimException) ex).getContext()); } else if (ex instanceof ParseException) { - DxConsole.err.println("\nPARSE ERROR:"); + context.err.println("\nPARSE ERROR:"); ParseException parseException = (ParseException) ex; if (args.debug) { - parseException.printStackTrace(DxConsole.err); + parseException.printStackTrace(context.err); } else { - parseException.printContext(DxConsole.err); + parseException.printContext(context.err); } } else { - DxConsole.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:"); - ex.printStackTrace(DxConsole.err); + context.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:"); + ex.printStackTrace(context.err); } errors.incrementAndGet(); } @@ -1679,13 +1724,13 @@ @Override public void onProcessArchiveStart(File file) { if (args.verbose) { - DxConsole.out.println("processing archive " + file + "..."); + context.out.println("processing archive " + file + "..."); } } } /** Callable helper class to parse class bytes. */ - private static class ClassParserTask implements Callable { + private class ClassParserTask implements Callable { String name; byte[] bytes; @@ -1718,7 +1763,7 @@ * and then reevaluate the invariant. If there are no further classes in * the translation phase, we rotate the dex file. */ - private static class DirectClassFileConsumer implements Callable { + private class DirectClassFileConsumer implements Callable { String name; byte[] bytes; @@ -1817,7 +1862,7 @@ /** Callable helper class to translate classes in parallel */ - private static class ClassTranslatorTask implements Callable { + private class ClassTranslatorTask implements Callable { String name; byte[] bytes; @@ -1844,7 +1889,7 @@ * This class is also responsible for coordinating dex file rotation * with the DirectClassFileConsumer class. */ - private static class ClassDefItemConsumer implements Callable { + private class ClassDefItemConsumer implements Callable { String name; Future futureClazz; @@ -1892,7 +1937,7 @@ } /** Callable helper class to convert dex files in worker threads */ - private static class DexWriter implements Callable { + private class DexWriter implements Callable { private DexFile dexFile; diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dump/BaseDumper.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dump/BaseDumper.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dump/BaseDumper.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dump/BaseDumper.java 2018-10-24 12:07:52.000000000 +0000 @@ -19,6 +19,7 @@ import com.android.dx.cf.code.ConcreteMethod; import com.android.dx.cf.iface.Member; import com.android.dx.cf.iface.ParseObserver; +import com.android.dx.dex.DexOptions; import com.android.dx.util.ByteArray; import com.android.dx.util.Hex; import com.android.dx.util.IndentingWriter; @@ -68,6 +69,9 @@ /** commandline parsedArgs */ protected Args args; + /** {@code non-null;} options for dex output, always set to the defaults for now */ + protected final DexOptions dexOptions; + /** * Constructs an instance. * @@ -90,6 +94,8 @@ this.at = 0; this.args = args; + this.dexOptions = new DexOptions(); + int hexCols = (((width - 5) / 15) + 1) & ~1; if (hexCols < 6) { hexCols = 6; diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dump/BlockDumper.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dump/BlockDumper.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dump/BlockDumper.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dump/BlockDumper.java 2018-10-24 12:07:52.000000000 +0000 @@ -28,6 +28,7 @@ import com.android.dx.cf.direct.StdAttributeFactory; import com.android.dx.cf.iface.Member; import com.android.dx.cf.iface.Method; +import com.android.dx.dex.DexOptions; import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.code.BasicBlock; import com.android.dx.rop.code.BasicBlockList; @@ -285,7 +286,7 @@ TranslationAdvice advice = DexTranslationAdvice.THE_ONE; BytecodeArray code = meth.getCode(); ByteArray bytes = code.getBytes(); - RopMethod rmeth = Ropper.convert(meth, advice, classFile.getMethods()); + RopMethod rmeth = Ropper.convert(meth, advice, classFile.getMethods(), dexOptions); StringBuffer sb = new StringBuffer(2000); if (optimize) { diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dump/DotDumper.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dump/DotDumper.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dump/DotDumper.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dump/DotDumper.java 2018-10-24 12:07:52.000000000 +0000 @@ -23,6 +23,7 @@ import com.android.dx.cf.iface.Member; import com.android.dx.cf.iface.Method; import com.android.dx.cf.iface.ParseObserver; +import com.android.dx.dex.DexOptions; import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.code.BasicBlock; import com.android.dx.rop.code.BasicBlockList; @@ -46,6 +47,7 @@ private final boolean strictParse; private final boolean optimize; private final Args args; + private final DexOptions dexOptions; static void dump(byte[] bytes, String filePath, Args args) { new DotDumper(bytes, filePath, args).run(); @@ -57,6 +59,7 @@ this.strictParse = args.strictParse; this.optimize = args.optimize; this.args = args; + this.dexOptions = new DexOptions(); } private void run() { @@ -115,7 +118,7 @@ TranslationAdvice advice = DexTranslationAdvice.THE_ONE; RopMethod rmeth = - Ropper.convert(meth, advice, classFile.getMethods()); + Ropper.convert(meth, advice, classFile.getMethods(), dexOptions); if (optimize) { boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags()); diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dump/Main.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dump/Main.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dump/Main.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dump/Main.java 2018-10-24 12:07:52.000000000 +0000 @@ -19,6 +19,7 @@ import com.android.dex.util.FileUtils; import com.android.dx.cf.iface.ParseException; import com.android.dx.util.HexParser; + import java.io.UnsupportedEncodingException; /** @@ -26,7 +27,7 @@ */ public class Main { - static Args parsedArgs = new Args(); + private Args parsedArgs = new Args(); /** * This class is uninstantiable. @@ -35,10 +36,14 @@ // This space intentionally left blank. } + public static void main(String[] args) { + new Main().run(args); + } + /** * Run! */ - public static void main(String[] args) { + private void run(String[] args) { int at = 0; for (/*at*/; at < args.length; at++) { @@ -112,7 +117,7 @@ * @param name {@code non-null;} name of the file * @param bytes {@code non-null;} contents of the file */ - private static void processOne(String name, byte[] bytes) { + private void processOne(String name, byte[] bytes) { if (parsedArgs.dotDump) { DotDumper.dump(bytes, name, parsedArgs); } else if (parsedArgs.basicBlocks) { diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dump/SsaDumper.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dump/SsaDumper.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/dump/SsaDumper.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/dump/SsaDumper.java 2018-10-24 12:07:52.000000000 +0000 @@ -90,7 +90,7 @@ ConcreteMethod meth = new ConcreteMethod((Method) member, classFile, true, true); TranslationAdvice advice = DexTranslationAdvice.THE_ONE; - RopMethod rmeth = Ropper.convert(meth, advice, classFile.getMethods()); + RopMethod rmeth = Ropper.convert(meth, advice, classFile.getMethods(), dexOptions); SsaMethod ssaMeth = null; boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags()); int paramWidth = computeParamWidth(meth, isStatic); diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/DxConsole.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/DxConsole.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/DxConsole.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/DxConsole.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2007 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.dx.command; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; - -/** - * Provides standard and error PrintStream object to output information.
- * By default the PrintStream objects link to {@code System.out} and - * {@code System.err} but they can be changed to link to other - * PrintStream. - */ -public class DxConsole { - /** - * Standard output stream. Links to {@code System.out} by default. - */ - public static PrintStream out = System.out; - - /** - * Error output stream. Links to {@code System.err} by default. - */ - public static PrintStream err = System.err; - - /** - * Output stream which prints to nowhere. - */ - public static final PrintStream noop = new PrintStream(new OutputStream() { - - @Override - public void write(int b) throws IOException { - // noop - } - }); -} diff -Nru android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/Main.java android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/Main.java --- android-platform-dalvik-7.0.0+r33/dx/src/com/android/dx/command/Main.java 2017-04-26 18:07:58.000000000 +0000 +++ android-platform-dalvik-8.1.0+r23/dx/src/com/android/dx/command/Main.java 2018-10-24 12:07:52.000000000 +0000 @@ -23,58 +23,46 @@ * to the right "actual" main. */ public class Main { - private static String USAGE_MESSAGE = + private static final String USAGE_MESSAGE = "usage:\n" + - " dx --dex [--debug] [--verbose] [--positions=