Guardsquare / proguard

ProGuard, Java optimizer and obfuscator
https://www.guardsquare.com/en/products/proguard
GNU General Public License v2.0
2.85k stars 408 forks source link

java.lang.VerifyError: Bad type on operand stack uninitializedThis #63

Open 502647092 opened 4 years ago

502647092 commented 4 years ago

if i close code/removal/advanced this error has gone

[13:05:00 ERROR]: Bad type on operand stack
Exception Details:
  Location:
    pw/yumc/SurvivalGamesKits/config/inject/InjectConfig.<init>(Lpw/yumc/SurvivalGamesKits/config/FileConfig;)V @2: invokevirtual
  Reason:
    Type uninitializedThis (current frame, stack[0]) is not assignable to 'pw/yumc/SurvivalGamesKits/config/inject/InjectConfig'
  Current Frame:
    bci: @2
    flags: { flagThisUninit }
    locals: { uninitializedThis, 'pw/yumc/SurvivalGamesKits/config/FileConfig' }
    stack: { uninitializedThis, 'pw/yumc/SurvivalGamesKits/config/FileConfig' }
  Bytecode:
    0x0000000: 2a2b b600 09b1
 initializing SurvivalGamesKits v1.3.5-git-INNER (Is it up to date?)
java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    pw/yumc/SurvivalGamesKits/config/inject/InjectConfig.<init>(Lpw/yumc/SurvivalGamesKits/config/FileConfig;)V @2: invokevirtual
  Reason:
    Type uninitializedThis (current frame, stack[0]) is not assignable to 'pw/yumc/SurvivalGamesKits/config/inject/InjectConfig'
  Current Frame:
    bci: @2
    flags: { flagThisUninit }
    locals: { uninitializedThis, 'pw/yumc/SurvivalGamesKits/config/FileConfig' }
    stack: { uninitializedThis, 'pw/yumc/SurvivalGamesKits/config/FileConfig' }
  Bytecode:
    0x0000000: 2a2b b600 09b1

        at pw.yumc.SurvivalGamesKits.SurvivalGamesKits.onLoad(SurvivalGamesKits.java:65) ~[?:?]
        at org.bukkit.craftbukkit.v1_12_R1.CraftServer.loadPlugins(CraftServer.java:310) [spigot-1.12.2.jar:git-Spigot-dcd1643-e60fc34]
        at net.minecraft.server.v1_12_R1.DedicatedServer.init(DedicatedServer.java:205) [spigot-1.12.2.jar:git-Spigot-dcd1643-e60fc34]
        at net.minecraft.server.v1_12_R1.MinecraftServer.run(MinecraftServer.java:545) [spigot-1.12.2.jar:git-Spigot-dcd1643-e60fc34]
        at java.lang.Thread.run(Thread.java:748) [?:1.8.0_221]
proguard.conf ``` # -----不缩小----- #-dontshrink # -----不优化----- #-dontoptimize -optimizationpasses 1 #-optimizations code/removal/advanced # -----忽略所有警告----- #-dontwarn -dontnote # -----混淆时应用侵入式重载----- -overloadaggressively # -----启用混淆字典----- -dontobfuscate -obfuscationdictionary obf.dict -classobfuscationdictionary obf.dict -packageobfuscationdictionary obf.dict # -----保留所有属性 -keepattributes ** # -----保护所有实体中的字段名称----- -keepclassmembers class * implements java.io.Serializable {;} # -----保护监听方法不被清理----- -keepclassmembers class * implements org.bukkit.event.Listener { @org.bukkit.event.EventHandler ; } -keepclassmembers class * implements net.md_5.bungee.api.plugin.Listener { @net.md_5.bungee.event.EventHandler ; } # -----保护继承事件不被清理----- -keep class ** extends org.bukkit.event.Event {*;} # -----保护枚举方法的完整性----- -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } # -----保护配置注入不被清理----- -keepclassmembers class * extends **.config.inject.Inject** { ; public (org.bukkit.configuration.ConfigurationSection); } # -----保护注解命令方法不被清理----- -keepclassmembers class **.commands.annotation.** {;} -keepclassmembers class * implements **.commands.interfaces.Executor {;} # -----保护注解NotProguard标记----- -keep class **.NotProguard -keep @**.NotProguard class * {*;} -keepclassmembers class * { @**.NotProguard ; @**.NotProguard ; } # -----清理调试日志----- -assumenosideeffects class **.bukkit.Log { public static void d(...); } # -----清理性能检测代码----- -assumenosideeffects class pw.yumc.SurvivalGamesKits.listener.misc.Timings { ; } ```
SurvivalGamesKits.java ``` package pw.yumc.SurvivalGamesKits; import java.io.File; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import me.wazup.survivalgames.SurvivalGames; import pw.yumc.SurvivalGamesKits.config.SGKConfig; import pw.yumc.SurvivalGamesKits.item.ItemManager; import pw.yumc.SurvivalGamesKits.listener.PlayerListener; import pw.yumc.YumCore.bukkit.Log; import pw.yumc.YumCore.commands.CommandSub; import pw.yumc.YumCore.commands.annotation.Cmd; import pw.yumc.YumCore.commands.annotation.Help; import pw.yumc.YumCore.commands.interfaces.Executor; import pw.yumc.YumCore.config.FileConfig; public class SurvivalGamesKits extends JavaPlugin implements Executor { private SurvivalGames sgMain; private FileConfig kits; private SGKConfig config; @Override public FileConfiguration getConfig() { return config.getConfig(); } public FileConfig getKits() { return kits; } public SGKConfig getSgkConfig() { return config; } public SurvivalGames getSgMain() { return sgMain; } @Override public void onEnable() { final Plugin plugin = Bukkit.getPluginManager().getPlugin("SurvivalGames"); if (plugin == null) { setEnabled(false); return; } File kitsFile = new File("plugins/SurvivalGames", "kits.yml"); if (kitsFile.exists()) { kits = new FileConfig(kitsFile); ItemManager.loadKits(); } else { Log.w("未找到 SurvivalGames 目录下的 kits.yml 文件. 如果是第一次启动 请稍候执行 reload 重新加载!"); } sgMain = (SurvivalGames) plugin; new CommandSub("SurvivalGamesKits", this); new PlayerListener(); } @Override public void onLoad() { config = new SGKConfig(); } @Cmd(permission = "SurvivalGamesKits.reload") @Help("重载配置文件") public void reload(CommandSender sender) { config.reload(); kits.reload(); ItemManager.loadKits(); Log.sender(sender, "§a配置文件已重载!"); } } ```
SGKConfig.java ``` package pw.yumc.SurvivalGamesKits.config; import pw.yumc.YumCore.config.annotation.ConfigNode; import pw.yumc.YumCore.config.inject.InjectConfig; /** * @author 喵♂呜 * @since 2016年9月19日 下午7:35:57 */ public class SGKConfig extends InjectConfig { @ConfigNode("Select.OriginTitle") private String sotitle; @ConfigNode("Select.NowTitle") private String sntitle; private Boolean GiveCompassAtStart; private String CompassName; private String Message; private String NoNext; private Boolean SavePage; /** * @return the compassName */ public String getCompassName() { return CompassName; } /** * @return the giveCompassAtStart */ public Boolean getGiveCompassAtStart() { return GiveCompassAtStart; } /** * @return the message */ public String getMessage() { return Message; } /** * @return the noNext */ public String getNoNext() { return NoNext; } /** * @return the savePage */ public Boolean isSavePage() { return SavePage; } /** * @return the sntitle */ public String getSelNowTitle() { return sntitle; } /** * @return the sotitle */ public String getSelOriTitle() { return sotitle; } public Boolean isGiveCompassAtStart() { return GiveCompassAtStart; } } ```
InjectConfig.java ``` package pw.yumc.YumCore.config.inject; import java.io.File; import pw.yumc.YumCore.config.FileConfig; /** * 配置自动载入类 * * @since 2016年7月5日 上午8:53:57 * @author 喵♂呜 */ public abstract class InjectConfig extends AbstractInjectConfig { public InjectConfig() { this(new FileConfig()); } public InjectConfig(File file) { this(new FileConfig(file)); } public InjectConfig(FileConfig config) { inject(config); } public InjectConfig(String name) { this(new FileConfig(name)); } /** * 获得配置文件 * * @return 配置文件 */ public FileConfig getConfig() { return (FileConfig) config; } /** * 重载配置文件 */ public void reload() { getConfig().reload(); inject(config); } /** * 自动化保存 */ public void save() { save(config); getConfig().save(); } } ```
FileConfig.java ``` package pw.yumc.YumCore.config; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; import org.apache.commons.lang.Validate; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.MemorySection; import org.bukkit.configuration.file.YamlConfiguration; import pw.yumc.YumCore.bukkit.Log; /** * 一个继承于 {@link YamlConfiguration} 的配置文件类 * 强制UTF-8编码处理所有的文件信息 * * @author 喵♂呜 * @version 1.0 * @since 2015年11月7日 下午2:36:07 */ public class FileConfig extends AbstractConfig { protected static String VERSION = "Version"; private static char ALT_COLOR_CHAR = '&'; private static String DEFAULT = "config.yml"; private static String DATA_FORMANT = "yyyyMMddHHmmss"; private static String CONFIG_BACKUP = "配置: %s 已备份为 %s !"; private static String CONFIG_UPDATED = "配置: %s 升级成功 版本 %s !"; private static String CONFIG_OVERRIDE = "配置: %s 将覆盖原有字段数据..."; private static String CONFIG_NOT_FOUND = "配置: 文件 %s 不存在!"; private static String CONFIG_READ_ERROR = "配置: %s 读取错误..."; private static String CONFIG_SAVE_ERROR = "配置: %s 保存错误..."; private static String CONFIG_UPDATE_WARN = "配置: %s 版本 %s 过低 正在升级到 %s ..."; private static String CONFIG_CREATE_ERROR = "配置: %s 创建失败..."; private static String CONFIG_FORMAT_ERROR = "配置: %s 格式错误..."; private static String CONFIG_BACKUP_ERROR = "配置: %s 备份失败 异常: %s !"; private static String CONFIG_UPDATE_VALUE = "配置: 更新字段 %s 的值为 %s ..."; private static String CONFIG_BACKUP_AND_RESET = "配置: %s 格式错误 已备份为 %s 并恢复默认配置!"; private static String CONFIG_NOT_FOUND_IN_JAR = "配置: 从插件内部未找到预置的 %s 文件!"; private static String CONFIG_READ_COMMENT_ERROR = "配置: 读取文件注释信息失败!"; private static String STREAM_NOT_BE_NULL = "数据流不能为 NULL"; protected File file; private CommentConfig commentConfig; /** * 实例化默认配置文件 */ public FileConfig() { this(DEFAULT); } /** * 从文件载入配置 * * @param file * 配置文件名称 */ public FileConfig(File file) { Validate.notNull(file, FILE_NOT_BE_NULL); init(file); } /** * 从文件载入配置 * * @param parent * 文件夹 * @param filename * 配置文件名称 */ public FileConfig(File parent, String filename) { init(new File(parent, filename), true); } /** * 从数据流载入配置文件 * * @param stream * 数据流 */ public FileConfig(InputStream stream) { init(stream); } /** * 从文件载入配置 * * @param filename * 配置文件名称 */ public FileConfig(String filename) { init(new File(plugin.getDataFolder(), filename), true); } /** * 从文件载入配置 * * @param parent * 文件夹 * @param filename * 配置文件名称 */ public FileConfig(String parent, String filename) { init(new File(parent, filename), true); } /** * 添加到List末尾 * * @param * List内容类型 * @param path * 路径 * @param obj * 对象 * @return {@link FileConfig} */ public FileConfig addToList(String path, E obj) { List l = (List) this.getList(path); if (null == l) { l = new ArrayList<>(); } l.add(obj); return this; } /** * 添加到StringList末尾 * * @param path * 路径 * @param obj * 字符串 * @return {@link FileConfig} */ public FileConfig addToStringList(String path, String obj) { addToStringList(path, obj, true); return this; } /** * 添加到StringList末尾 * * @param path * 路径 * @param obj * 字符串 * @param allowrepeat * 是否允许重复 * @return {@link FileConfig} */ public FileConfig addToStringList(String path, String obj, boolean allowrepeat) { List l = this.getStringList(path); if (null == l) { l = new ArrayList<>(); } if (allowrepeat || !l.contains(obj)) { l.add(obj); } this.set(path, l); return this; } /** * 获得已颜色转码的文本 * * @param cfgmsg * 待转码的List * @return 颜色转码后的文本 */ public List getColorList(List cfgmsg) { List message = new ArrayList<>(); if (cfgmsg == null) { return Collections.emptyList(); } for (String msg : cfgmsg) { message.add(ChatColor.translateAlternateColorCodes('&', msg)); } return message; } /** * 获得配置文件名称 * * @return 配置文件名称 */ public String getConfigName() { return file.getName(); } /** * 获得Location * * @param key * 键 * @return {@link Location} */ public Location getLocation(String key) { return getLocation(key, null); } /** * 获得Location * * @param path * 键 * @param def * 默认地点 * @return {@link Location} */ public Location getLocation(String path, Location def) { Object val = get(path, def); return val instanceof Location ? (Location) val : def; } /** * 获得已颜色转码的文本 * * @param path * 配置路径 * @return 颜色转码后的文本 */ public String getMessage(String path) { return getMessage(path, null); } /** * 获得已颜色转码的文本 * * @param path * 配置路径 * @param def * 默认文本 * @return 颜色转码后的文本 */ public String getMessage(String path, String def) { String message = this.getString(path, def); if (message != null) { message = ChatColor.translateAlternateColorCodes('&', message); } return message; } /** * 获得已颜色转码的文本 * * @param path * 配置路径 * @return 颜色转码后的文本 */ public List getMessageList(String path) { List cfgmsg = this.getStringList(path); if (cfgmsg == null) { return Collections.emptyList(); } for (int i = 0; i < cfgmsg.size(); i++) { cfgmsg.set(i, ChatColor.translateAlternateColorCodes(ALT_COLOR_CHAR, cfgmsg.get(i))); } return cfgmsg; } /** * 获得字符串数组 * * @param path * 配置路径 * @return 字符串数组 */ public String[] getStringArray(String path) { return this.getStringList(path).toArray(new String[0]); } @Override public void loadFromString(String contents) throws InvalidConfigurationException { try { commentConfig = new CommentConfig(); commentConfig.loadFromString(contents); } catch (Exception e) { Log.d(CONFIG_READ_COMMENT_ERROR); commentConfig = null; } super.loadFromString(contents); } /** * 比较版本号 * * @param newver * 新版本 * @param oldver * 旧版本 * @return 是否需要更新 */ public boolean needUpdate(String newver, String oldver) { if (newver == null) { return false; } if (oldver == null) { return true; } String[] va1 = newver.split("\\.");// 注意此处为正则匹配,不能用"."; String[] va2 = oldver.split("\\."); int idx = 0; int minLength = Math.min(va1.length, va2.length);// 取最小长度值 int diff = 0; while (idx < minLength && (diff = va1[idx].length() - va2[idx].length()) == 0// 先比较长度 && (diff = va1[idx].compareTo(va2[idx])) == 0) {// 再比较字符 ++idx; } // 如果已经分出大小,则直接返回,如果未分出大小,则再比较位数,有子版本的为大; diff = (diff != 0) ? diff : va1.length - va2.length; return diff > 0; } /** * 重新载入配置文件 * * @return 是否载入成功 */ public boolean reload() { return init(file) != null; } /** * 从List移除对象 * * @param * List内容对象类型 * @param path * 路径 * @param obj * 对象 * @return {@link FileConfig} */ public FileConfig removeFromList(String path, E obj) { List l = (List) this.getList(path); if (null != l) { l.remove(obj); } return this; } /** * 从StringList移除对象 * * @param path * 路径 * @param obj * 对象 * @return {@link FileConfig} */ public FileConfig removeFromStringList(String path, String obj) { List l = this.getStringList(path); if (null != l) { l.remove(obj); } this.set(path, obj); return this; } /** * 快速保存配置文件 * * @return 是否成功 */ public boolean save() { try { this.save(file); return true; } catch (IOException e) { Log.w(CONFIG_SAVE_ERROR, file.getName()); e.printStackTrace(); return false; } } @Override public void save(File file) throws IOException { Validate.notNull(file, FILE_NOT_BE_NULL); if (commentConfig != null) { data = commentConfig.saveToString(); } else { data = saveToString(); } super.save(file); } @Override public void set(String path, Object value) { if (commentConfig != null) { commentConfig.set(path, value); } super.set(path, value); } /** * 从Jar保存配置文件 */ private void saveFromJar() { if (plugin != null && file != null) { try { String filename = file.getName(); InputStream filestream = plugin.getResource(file.getName()); String errFileName = this.getErrName(filename); file.renameTo(new File(file.getParent(), errFileName)); if (filestream == null) { file.createNewFile(); } else { plugin.saveResource(filename, true); } Log.w(CONFIG_BACKUP_AND_RESET, filename, errFileName); } catch (IOException | IllegalArgumentException e) { throw new IllegalArgumentException(e); } } else { Log.w(CONFIG_NOT_FOUND_IN_JAR, file != null ? file.getName() : ""); } } /** * 备份配置文件 * * @param oldcfg * 配置文件 */ protected void backupConfig(FileConfig oldcfg) { String filename = oldcfg.getConfigName(); try { String newCfgName = this.getBakName(filename); File newcfg = new File(file.getParent(), newCfgName); oldcfg.save(newcfg); Log.w(CONFIG_BACKUP, filename, newCfgName); } catch (IOException e) { Log.w(CONFIG_BACKUP_ERROR, filename, e.getMessage()); Log.d(oldcfg.getConfigName(), e); } } /** * 检查配置文件 * * @param file * 配置文件 */ protected void check(File file) { String filename = file.getName(); InputStream stream = plugin.getResource(filename); try { if (!file.exists()) { file.getParentFile().mkdirs(); if (stream == null) { file.createNewFile(); } else { plugin.saveResource(filename, true); } } else { if (stream == null) { return; } FileConfig newcfg = new FileConfig(stream); FileConfig oldcfg = new FileConfig(file); if (needUpdate(newcfg, oldcfg)) { backupConfig(oldcfg); updateConfig(newcfg, oldcfg).save(file); } } } catch (IOException e) { Log.w(CONFIG_CREATE_ERROR, filename); } } protected String getBakName(String cfgname) { return cfgname + "." + getStringDate(DATA_FORMANT) + ".bak"; } protected String getErrName(String cfgname) { return cfgname + "." + getStringDate(DATA_FORMANT) + ".err"; } /** * 获取现在时间 * * @param format * 字符串格式 * @return yyyy-MM-dd HH:mm:ss */ protected String getStringDate(String format) { if (format == null) { format = "yyyy-MM-dd HH:mm:ss"; } return new SimpleDateFormat(format).format(new Date()); } /** * 初始化FileConfig * * @param file * 配置文件 * @return FileConfig */ protected FileConfig init(File file) { init(file, false); return this; } /** * 初始化FileConfig * * @param file * 配置文件 * @param check * 是否检查文件 * @return FileConfig */ protected FileConfig init(File file, boolean check) { Validate.notNull(file, FILE_NOT_BE_NULL); this.file = file; if (check) { check(file); } try { init(new FileInputStream(file)); } catch (FileNotFoundException e) { Log.d(CONFIG_NOT_FOUND, file.toPath()); } return this; } /** * 初始化FileConfig * * @param stream * 输入流 * @return FileConfig */ protected FileConfig init(InputStream stream) { Validate.notNull(stream, STREAM_NOT_BE_NULL); try { this.load(new InputStreamReader(stream, UTF_8)); } catch (InvalidConfigurationException | IllegalArgumentException ex) { if (file == null) { throw new IllegalArgumentException(ex); } Log.w(CONFIG_FORMAT_ERROR, file.getName()); Log.w(ex.getMessage()); saveFromJar(); } catch (IOException ex) { if (file == null) { throw new IllegalStateException(ex); } Log.w(CONFIG_READ_ERROR, file.getName()); } return this; } /** * 检查配置文件版本 * * @param newcfg * 新配置文件 * @param oldcfg * 旧配置文件 * @return 是否需要升级 */ protected boolean needUpdate(FileConfig newcfg, FileConfig oldcfg) { return needUpdate(newcfg.getString(VERSION), oldcfg.getString(VERSION)); } /** * 更新配置文件 * * @param newCfg * 新配置文件 * @param oldCfg * 旧配置文件 * @return 更新以后的配置文件 */ protected FileConfig updateConfig(FileConfig newCfg, FileConfig oldCfg) { return updateConfig(newCfg, oldCfg, false); } /** * 更新配置文件 * * @param newCfg * 新的配置文件 * @param oldCfg * 老的配置文件 * @param force * 是否强制更新 * @return 更新以后的配置文件 */ protected FileConfig updateConfig(FileConfig newCfg, FileConfig oldCfg, boolean force) { String filename = oldCfg.getConfigName(); String newver = newCfg.getString(VERSION); String oldver = oldCfg.getString(VERSION); Set oldConfigKeys = oldCfg.getKeys(true); Log.w(CONFIG_UPDATE_WARN, filename, oldver, newver); // 保留版本字段 不更新 oldConfigKeys.remove(VERSION); // 强制更新 去除新版本存在的字段 if (force) { Log.w(CONFIG_OVERRIDE, filename); oldConfigKeys.removeAll(newCfg.getKeys(true)); } // 复制旧的数据 for (String string : oldConfigKeys) { Object var = oldCfg.get(string); // 需要进行节点检查 还有类型检查 不同类型情况下 使用新配置 if (var != null && !(var instanceof MemorySection)) { Object newVer = newCfg.get(string); if (newVer != null && !newVer.getClass().equals(var.getClass())) { Log.w("警告! 旧数据类型与新配置类型不匹配!"); } Log.d(CONFIG_UPDATE_VALUE, string, var); newCfg.set(string, var); } } Log.i(CONFIG_UPDATED, filename, newver); return newCfg; } } ```
EricLafortune commented 4 years ago

Thanks for your report. The problem is probably caused by ProGuard inlining inject from the superclass AbstractInjectConfig in InjectConfig:

    public InjectConfig(FileConfig config) {
        inject(config);
    }

I can't reproduce it yet. If you can share these two compiled class files, that may help.

For now, you can probably work around it by keeping the inject method

-keepclassmembers class pw.yumc.SurvivalGamesKits.config.inject.AbstractInjectConfig { *** inject(...); }

or by disabling method inlining

-optimizations !method/inlining/*