net.md_5.specialsource.AccessMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of SpecialSource Show documentation
Show all versions of SpecialSource Show documentation
A jar compare and renaming engine designed for comparing and remapping 2 jars of differnent obfuscation mappings. Can also be useful for reobfuscation.
The newest version!
/**
* Copyright (c) 2012, md_5. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* The name of the author may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package net.md_5.specialsource;
import net.md_5.specialsource.util.FileLocator;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import lombok.Getter;
/**
* Access mapper - for modifying access flags on symbols
*
* Supports loading _at.cfg files in the following format:
*
* - comments beginning with '#' extending to end of line
* - symbol pattern, space, then access changes
*
*
* Symbol pattern format:
*
* foo class
* foo/bar field
* foo/bar ()desc method
* foo/* fields in class
* foo/* ()desc methods in class
* * all classes
* */* all fields
* */*() all methods
* ** all classes, fields, and methods
*
* Internal ('/') and source ('.') conventions are accepted,
* and the space preceding the method descriptor is optional.
*
* Access change format: visibility (required) + access flags
* @see AccessChange
*/
public class AccessMap {
@Getter
private Map map = new HashMap();
@Getter
private Set appliedMaps = new HashSet();
public AccessMap() {
}
public void loadAccessTransformer(BufferedReader reader) throws IOException {
String line;
while ((line = reader.readLine()) != null) {
// strip comments and trailing whitespace
int n = line.indexOf('#');
if (n != -1) {
line = line.substring(0, n);
}
line = line.trim();
if (line.isEmpty()) {
continue;
}
addAccessChange(line);
}
}
public void loadAccessTransformer(File file) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
loadAccessTransformer(reader);
}
}
/**
* Load an access transformer into this AccessMap.
*
* @param filename Location of AT data, one of: - local filename - remote
* HTTP URL - "pattern:" followed by one transformer line
* @throws IOException
*/
public void loadAccessTransformer(String filename) throws IOException {
if (filename.startsWith("pattern:")) {
addAccessChange(filename.substring("pattern:".length()));
} else {
loadAccessTransformer(FileLocator.getFile(filename));
}
}
/**
* Convert a symbol name pattern from AT config to internal format
*/
public static String convertSymbolPattern(String s) {
// source name to internal name
s = s.replace('.', '/');
// method descriptor separated from name by a space
if (s.indexOf('(') != -1) {
s = s.replaceFirst("(?=[^ ])[(]", " (");
}
// now it matches the symbol name format used in the rest of SpecialSource
// (but also possibly with wildcards)
return s;
}
public void addAccessChange(String line) {
// _at.cfg format:
// protected/public/private[+/-modifiers] symbol
int n = line.indexOf(' ');
if (n == -1) {
throw new IllegalArgumentException("loadAccessTransformer invalid line: " + line);
}
String accessString = line.substring(0, n);
String symbolString = line.substring(n + 1);
addAccessChange(symbolString, accessString);
}
public void addAccessChange(String symbolString, String accessString) {
addAccessChange(convertSymbolPattern(symbolString), new AccessChange(accessString));
}
public void addAccessChange(String key, AccessChange accessChange) {
if (map.containsKey(key)) {
System.out.println("INFO: merging AccessMap " + key + " from " + map.get(key) + " with " + accessChange);
map.get(key).merge(accessChange);
} else {
map.put(key, accessChange);
}
}
public int applyClassAccess(String className, int access) {
int old = access;
access = apply("**", access);
access = apply("*", access);
access = apply(className, access);
//System.out.println("AT: class: "+className+" "+old+" -> "+access); // TODO: debug logging
return access;
}
public int applyFieldAccess(String className, String fieldName, int access) {
int old = access;
access = apply("**", access);
access = apply("*/*", access);
access = apply(className + "/*", access);
access = apply(className + "/" + fieldName, access);
//System.out.println("AT: field: "+className+"/"+fieldName+" "+old+" -> "+access);
return access;
}
public int applyMethodAccess(String className, String methodName, String methodDesc, int access) {
int old = access;
access = apply("**", access);
access = apply("*/* ()", access);
access = apply(className + "/* ()", access);
access = apply(className + "/" + methodName + " " + methodDesc, access);
// if (access!= old) System.out.println("AT: method: "+className+"/"+methodName+" "+methodDesc+" "+old+" -> "+access);
return access;
}
private int apply(String key, int existing) {
AccessChange change = map.get(key);
if (change == null) {
return existing;
} else {
int newAccess = change.apply(existing);
if (newAccess != existing) {
appliedMaps.add(key);
}
accessApplied(key, existing, newAccess);
return newAccess;
}
}
/**
* Called when an access mapping is applied.
*
* @param key the key which caused the mapping to be matched and applied
* @param oldAccess the access which was replaced
* @param newAccess the new access which was applied by the mapping
*/
protected void accessApplied(String key, int oldAccess, int newAccess) {
// Override
}
}