Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making the project build and speeding up jarjar with optimization #10

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

<property name="javadoc.access" value="public"/>

<property name="compile.source" value="1.6"/>
<property name="compile.target" value="1.6"/>
<property name="compile.source" value="1.8"/>
<property name="compile.target" value="1.8"/>
<property name="compile.bootclasspath" value=""/>
<property name="compile.extdirs" value=""/>

Expand Down
6 changes: 3 additions & 3 deletions src/main/com/tonicsystems/jarjar/PackageRemapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ class PackageRemapper extends Remapper {
private static final Pattern ARRAY_FOR_NAME_PATTERN =
Pattern.compile("\\[L[\\p{javaJavaIdentifierPart}\\.]+?;");

private final List<Wildcard> wildcards;
private final WildcardTrie wildcards;
private final Map<String, String> typeCache = new HashMap<>();
private final Map<String, String> pathCache = new HashMap<>();
private final Map<Object, String> valueCache = new HashMap<>();
private final boolean verbose;

public PackageRemapper(List<Rule> ruleList, boolean verbose) {
this.verbose = verbose;
wildcards = PatternElement.createWildcards(ruleList);
wildcards = new WildcardTrie(PatternElement.createWildcards(ruleList));
}

// also used by KeepProcessor
Expand Down Expand Up @@ -128,7 +128,7 @@ public Object mapValue(Object value) {
}

private String replaceHelper(String value) {
for (Wildcard wildcard : wildcards) {
for (Wildcard wildcard : wildcards.getPossibleMatches(value)) {
String test = wildcard.replace(value);
if (test != null) {
return test;
Expand Down
4 changes: 3 additions & 1 deletion src/main/com/tonicsystems/jarjar/PatternElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ public String getPattern() {

static List<Wildcard> createWildcards(List<? extends PatternElement> patterns) {
List<Wildcard> wildcards = new ArrayList<>();
int ruleIndex = 0;
for (PatternElement pattern : patterns) {
String result = (pattern instanceof Rule) ? ((Rule) pattern).getResult() : "";
String expr = pattern.getPattern();
if (expr.indexOf('/') >= 0) {
throw new IllegalArgumentException("Patterns cannot contain slashes");
}
wildcards.add(new Wildcard(expr.replace('.', '/'), result));
wildcards.add(new Wildcard(expr.replace('.', '/'), result, ruleIndex));
ruleIndex++;
}
return wildcards;
}
Expand Down
21 changes: 20 additions & 1 deletion src/main/com/tonicsystems/jarjar/Wildcard.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,18 @@ class Wildcard {
private static final Pattern DSTAR = Pattern.compile("\\*\\*");
private static final Pattern STAR = Pattern.compile("\\*");
private static final Pattern ESTAR = Pattern.compile("\\+\\??\\)\\Z");
// Apart from stars and dollar signs, wildcards are plain-text full matches
private static Pattern PLAIN_TEXT_PREFIX = Pattern.compile("^[^*$]*");

private final Pattern pattern;
private final String plainTextPrefix;
private final int ruleIndex;
private final int count;
private final ArrayList<Object> parts = new ArrayList<>(16); // kept for debugging
private final String[] strings;
private final int[] refs;

public Wildcard(String pattern, String result) {
public Wildcard(String pattern, String result, int ruleIndex) {
if (pattern.equals("**")) {
throw new IllegalArgumentException("'**' is not a valid pattern");
}
Expand All @@ -47,6 +51,13 @@ public Wildcard(String pattern, String result) {
regex = replaceAllLiteral(DSTAR, regex, "(.+?)");
regex = replaceAllLiteral(STAR, regex, "([^/]+)");
regex = replaceAllLiteral(ESTAR, regex, "*)");
Matcher prefixMatcher = PLAIN_TEXT_PREFIX.matcher(pattern);
// prefixMatcher will always match, but may match an empty string
if (!prefixMatcher.find()) {
throw new IllegalArgumentException(PLAIN_TEXT_PREFIX + " not found in " + pattern);
}
this.plainTextPrefix = prefixMatcher.group();
this.ruleIndex = ruleIndex;
this.pattern = Pattern.compile("\\A" + regex + "\\Z");
this.count = this.pattern.matcher("foo").groupCount();

Expand Down Expand Up @@ -107,6 +118,14 @@ public Wildcard(String pattern, String result) {
// System.err.println(this);
}

public String getPlainTextPrefix() {
return plainTextPrefix;
}

public int getRuleIndex() {
return ruleIndex;
}

public boolean matches(String value) {
return getMatcher(value) != null;
}
Expand Down
91 changes: 91 additions & 0 deletions src/main/com/tonicsystems/jarjar/WildcardTrie.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright (C) 2022 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.tonicsystems.jarjar;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.TreeMap;

/**
* A prefix trie of {@link Wildcard}, where the prefix is obtained from
* {@link Wildcard#getPlainTextPrefix()}.
*
* This allows quick lookup of applicable wildcards in the common case where wildcards have a
* non-empty plain-text prefix.
*/
public class WildcardTrie {
private final TreeMap<String, WildcardTrie> subTries = new TreeMap<>();
private final List<Wildcard> wildcards = new ArrayList<>();
private final String prefix;

public WildcardTrie(List<Wildcard> wildcards) {
this("");
final ArrayList<Wildcard> lst = new ArrayList<>(wildcards);
// Sort values to ensure that wildcards that prefix others are added first
lst.sort(Comparator.comparing(Wildcard::getPlainTextPrefix));
for (Wildcard w : lst) {
final String prefix = w.getPlainTextPrefix();
final WildcardTrie prefixTrie = findSubTrieWhichPrefixes(prefix, this);
if (prefixTrie.prefix.equals(prefix)) {
prefixTrie.wildcards.add(w);
} else {
final WildcardTrie newTrie = new WildcardTrie(prefix);
newTrie.wildcards.add(w);
prefixTrie.subTries.put(prefix, newTrie);
}
}
}

private WildcardTrie(String prefix) {
this.prefix = prefix;
}

private static WildcardTrie findSubTrieWhichPrefixes(String value, WildcardTrie baseTrie) {
final String possiblePrefix = baseTrie.subTries.floorKey(value);
// Because each level of the trie does not contain keys that are prefixes of each other,
// there can be at most one prefix of the value at that level, and that prefix will be the
// highest key ordered before the value (any non-prefix key would have a character
// difference with the prefix and so be ordered before the prefix or after the value).
if (possiblePrefix != null && value.startsWith(possiblePrefix)) {
return findSubTrieWhichPrefixes(value, baseTrie.subTries.get(possiblePrefix));
}
return baseTrie;
}

public List<Wildcard> getPossibleMatches(String value) {
WildcardTrie baseTrie = this;
List<Wildcard> prefixMatches = wildcards.isEmpty()
// If there's no match, don't even allocate a list and use the singleton emptyList
? Collections.emptyList() : new ArrayList<>(wildcards);
while (true) {
final String possiblePrefix = baseTrie.subTries.floorKey(value);
if (possiblePrefix != null && value.startsWith(possiblePrefix)) {
baseTrie = baseTrie.subTries.get(possiblePrefix);
if (prefixMatches.isEmpty()) {
prefixMatches = new ArrayList<>(baseTrie.wildcards);
} else {
prefixMatches.addAll(baseTrie.wildcards);
}
} else {
prefixMatches.sort(Comparator.comparing(Wildcard::getRuleIndex));
return prefixMatches;
}
}
}
}