我正在使用开源库 Btrace 来分析 Java 应用程序。
我一直在搞砸它,因为我发现了一些错误,并且在尝试修复它们时,我遇到了一个神秘的事件。
本质上,我使用的是带有标志的 ClassWriter,COMPUTE_MAXS
我的目的不是弄乱现有方法的 StackMapTables(在任何情况下)
但是,似乎虽然在COMPUTE_FRAMES
某些情况下它可能不会重新计算 StackMapTables(会这样做),但实际的 StackMapTable 是“缺失的”
这是一个例子。
原始代码:
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.common.command;
import static com.google.common.base.Preconditions.checkNotNull;
import net.minecraft.command.ICommandSender;
import net.minecraft.util.math.Vec3d;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.world.Locatable;
import org.spongepowered.api.service.permission.MemorySubjectData;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.channel.MessageChannel;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;
import org.spongepowered.common.SpongeImpl;
import org.spongepowered.common.interfaces.IMixinCommandSender;
import org.spongepowered.common.service.permission.SpongePermissionService;
import org.spongepowered.common.service.permission.base.SpongeSubject;
import org.spongepowered.common.text.SpongeTexts;
import org.spongepowered.common.util.VecHelper;
import java.util.Optional;
public class WrapperCommandSource extends SpongeSubject implements CommandSource {
final ICommandSender sender;
private final MemorySubjectData data;
private WrapperCommandSource(ICommandSender sender) {
this.sender = sender;
this.data = new MemorySubjectData(SpongeImpl.getGame().getServiceManager().provide(PermissionService.class).get());
CommandPermissions.populateMinecraftPermissions(sender, data);
}
@Override
public String getIdentifier() {
return this.sender.getName();
}
@Override
public Optional<CommandSource> getCommandSource() {
return Optional.of((CommandSource) this);
}
@Override
public SubjectCollection getContainingCollection() {
SpongePermissionService permission = (SpongePermissionService) SpongeImpl.getGame().getServiceManager().provide(PermissionService.class).get();
return permission.getSubjects(PermissionService.SUBJECTS_SYSTEM);
}
@Override
public MemorySubjectData getSubjectData() {
return this.data;
}
@Override
public String getName() {
return this.sender.getName();
}
@Override
public void sendMessage(Text message) {
checkNotNull(message, "message");
this.sender.sendMessage(SpongeTexts.toComponent(message));
}
@Override
public MessageChannel getMessageChannel() {
return SpongeImpl.getGame().getServer().getBroadcastChannel();
}
@Override
public void setMessageChannel(MessageChannel channel) {
// TODO Should I throw an exception here?
}
public static CommandSource of(ICommandSender sender) {
if (sender instanceof IMixinCommandSender) {
return ((IMixinCommandSender) sender).asCommandSource();
}
if (sender instanceof WrapperICommandSender) {
return ((WrapperICommandSender) sender).source;
}
if (sender.getCommandSenderEntity() != null || !Vec3d.ZERO.equals(sender.getPositionVector())) {
return new Located(sender);
}
return new WrapperCommandSource(sender);
}
public static class Located extends WrapperCommandSource implements Locatable {
Located(ICommandSender sender) {
super(sender);
}
@Override
public Location<World> getLocation() {
return new Location<>((World) this.sender.getEntityWorld(), VecHelper.toVector3d(this.sender.getPositionVector()));
}
}
}
使用命令javap -p -v -c <class>
,我正在查看类中每个方法的 StackMapTables - 遗憾的是,完整的输出太长了,无法放在这里,但我会提出我想要关注的重点,主要是类的构造函数。
原始构造函数:
private org.spongepowered.common.command.WrapperCommandSource(net.minecraft.command.ICommandSender);
descriptor: (Lnet/minecraft/command/ICommandSender;)V
flags: ACC_PRIVATE
Code:
stack=6, locals=5, args_size=2
0: aload_0
1: invokespecial #26 // Method org/spongepowered/common/service/permission/base/SpongeSubject."<init>":()V
4: aload_0
5: aload_1
6: putfield #28 // Field sender:Lnet/minecraft/command/ICommandSender;
9: aload_0
10: new #30 // class org/spongepowered/api/service/permission/MemorySubjectData
13: dup
14: invokestatic #36 // Method org/spongepowered/common/SpongeImpl.getGame:()Lorg/spongepowered/common/SpongeGame;
17: invokevirtual #42 // Method org/spongepowered/common/SpongeGame.getServiceManager:()Lorg/spongepowered/api/service/ServiceManager;
20: ldc #44 // class org/spongepowered/api/service/permission/PermissionService
22: invokeinterface #50, 2 // InterfaceMethod org/spongepowered/api/service/ServiceManager.provide:(Ljava/lang/Class;)Ljava/util/Optional;
27: invokevirtual #56 // Method java/util/Optional.get:()Ljava/lang/Object;
30: checkcast #44 // class org/spongepowered/api/service/permission/PermissionService
33: invokespecial #59 // Method org/spongepowered/api/service/permission/MemorySubjectData."<init>":(Lorg/spongepowered/api/service/permission/PermissionService;)V
36: putfield #61 // Field data:Lorg/spongepowered/api/service/permission/MemorySubjectData;
39: aload_0
40: getfield #61 // Field data:Lorg/spongepowered/api/service/permission/MemorySubjectData;
43: aload_0
44: getfield #28 // Field sender:Lnet/minecraft/command/ICommandSender;
47: dup
48: invokevirtual #67 // Method java/lang/Object.getClass:()Ljava/lang/Class;
51: pop
52: invokedynamic #89, 0 // InvokeDynamic #0:apply:(Lnet/minecraft/command/ICommandSender;)Ljava/util/function/BiFunction;
57: invokestatic #95 // Method org/spongepowered/common/command/CommandPermissions.populateNonCommandPermissions:(Lorg/spongepowered/api/service/permission/SubjectData;Ljava/util/function/BiFunction;)V
60: invokestatic #36 // Method org/spongepowered/common/SpongeImpl.getGame:()Lorg/spongepowered/common/SpongeGame;
63: invokevirtual #99 // Method org/spongepowered/common/SpongeGame.getCommandManager:()Lorg/spongepowered/api/command/CommandManager;
66: invokeinterface #105, 1 // InterfaceMethod org/spongepowered/api/command/CommandManager.getCommands:()Ljava/util/Set;
71: invokeinterface #111, 1 // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
76: astore_2
77: aload_2
78: invokeinterface #117, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
83: ifeq 158
86: aload_2
87: invokeinterface #120, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
92: checkcast #122 // class org/spongepowered/api/command/CommandMapping
95: astore_3
96: aload_3
97: invokeinterface #126, 1 // InterfaceMethod org/spongepowered/api/command/CommandMapping.getCallable:()Lorg/spongepowered/api/command/CommandCallable;
102: instanceof #128 // class org/spongepowered/common/command/MinecraftCommandWrapper
105: ifeq 155
108: aload_3
109: invokeinterface #126, 1 // InterfaceMethod org/spongepowered/api/command/CommandMapping.getCallable:()Lorg/spongepowered/api/command/CommandCallable;
114: checkcast #128 // class org/spongepowered/common/command/MinecraftCommandWrapper
117: astore 4
119: aload_0
120: getfield #61 // Field data:Lorg/spongepowered/api/service/permission/MemorySubjectData;
123: getstatic #134 // Field org/spongepowered/api/service/permission/SubjectData.GLOBAL_CONTEXT:Ljava/util/Set;
126: aload 4
128: invokevirtual #138 // Method org/spongepowered/common/command/MinecraftCommandWrapper.getCommandPermission:()Ljava/lang/String;
131: aload 4
133: getfield #142 // Field org/spongepowered/common/command/MinecraftCommandWrapper.command:Lnet/minecraft/command/ICommand;
136: aload_1
137: invokeinterface #146, 1 // InterfaceMethod net/minecraft/command/ICommandSender.func_184102_h:()Lnet/minecraft/server/MinecraftServer;
142: aload_1
143: invokeinterface #152, 3 // InterfaceMethod net/minecraft/command/ICommand.func_184882_a:(Lnet/minecraft/server/MinecraftServer;Lnet/minecraft/command/ICommandSender;)Z
148: invokestatic #158 // Method org/spongepowered/api/util/Tristate.fromBoolean:(Z)Lorg/spongepowered/api/util/Tristate;
151: invokevirtual #162 // Method org/spongepowered/api/service/permission/MemorySubjectData.setPermission:(Ljava/util/Set;Ljava/lang/String;Lorg/spongepowered/api/util/Tristate;)Z
154: pop
155: goto 77
158: return
LocalVariableTable:
Start Length Slot Name Signature
119 36 4 wrapper Lorg/spongepowered/common/command/MinecraftCommandWrapper;
96 59 3 command Lorg/spongepowered/api/command/CommandMapping;
0 159 0 this Lorg/spongepowered/common/command/WrapperCommandSource;
0 159 1 sender Lnet/minecraft/command/ICommandSender;
LineNumberTable:
line 56: 0
line 57: 4
line 58: 9
line 62: 39
line 63: 60
line 64: 96
line 65: 108
line 66: 119
line 67: 137
line 66: 151
line 69: 155
line 70: 158
StackMapTable: number_of_entries = 3
frame_type = 255 /* full_frame */
offset_delta = 77
locals = [ class org/spongepowered/common/command/WrapperCommandSource, class net/minecraft/command/ICommandSender, class java/util/Iterator ]
stack = []
frame_type = 251 /* same_frame_extended */
offset_delta = 77
frame_type = 250 /* chop */
offset_delta = 2
MethodParameters:
Name Flags
sender
转换构造函数:
private org.spongepowered.common.command.WrapperCommandSource(net.minecraft.command.ICommandSender);
descriptor: (Lnet/minecraft/command/ICommandSender;)V
flags: ACC_PRIVATE
Code:
stack=6, locals=9, args_size=2
0: aload_0
1: invokespecial #26 // Method org/spongepowered/common/service/permission/base/SpongeSubject."<init>":()V
4: ldc_w #282 // String private void org.spongepowered.common.command.WrapperCommandSource#<init>(net.minecraft.command.ICommandSender)
7: invokestatic #286 // Method $btrace$com$negafinity$btrace$Logger$entry:(Ljava/lang/String;)V
10: lconst_0
11: lstore_2
12: invokestatic #292 // Method java/lang/System.nanoTime:()J
15: lstore 4
17: aload_0
18: aload_1
19: putfield #28 // Field sender:Lnet/minecraft/command/ICommandSender;
22: aload_0
23: new #30 // class org/spongepowered/api/service/permission/MemorySubjectData
26: dup
27: invokestatic #36 // Method org/spongepowered/common/SpongeImpl.getGame:()Lorg/spongepowered/common/SpongeGame;
30: invokevirtual #42 // Method org/spongepowered/common/SpongeGame.getServiceManager:()Lorg/spongepowered/api/service/ServiceManager;
33: ldc #44 // class org/spongepowered/api/service/permission/PermissionService
35: invokeinterface #50, 2 // InterfaceMethod org/spongepowered/api/service/ServiceManager.provide:(Ljava/lang/Class;)Ljava/util/Optional;
40: invokevirtual #56 // Method java/util/Optional.get:()Ljava/lang/Object;
43: checkcast #44 // class org/spongepowered/api/service/permission/PermissionService
46: invokespecial #59 // Method org/spongepowered/api/service/permission/MemorySubjectData."<init>":(Lorg/spongepowered/api/service/permission/PermissionService;)V
49: putfield #61 // Field data:Lorg/spongepowered/api/service/permission/MemorySubjectData;
52: aload_0
53: getfield #61 // Field data:Lorg/spongepowered/api/service/permission/MemorySubjectData;
56: aload_0
57: getfield #28 // Field sender:Lnet/minecraft/command/ICommandSender;
60: dup
61: invokevirtual #67 // Method java/lang/Object.getClass:()Ljava/lang/Class;
64: pop
65: invokedynamic #89, 0 // InvokeDynamic #0:apply:(Lnet/minecraft/command/ICommandSender;)Ljava/util/function/BiFunction;
70: invokestatic #95 // Method org/spongepowered/common/command/CommandPermissions.populateNonCommandPermissions:(Lorg/spongepowered/api/service/permission/SubjectData;Ljava/util/function/BiFunction;)V
73: invokestatic #36 // Method org/spongepowered/common/SpongeImpl.getGame:()Lorg/spongepowered/common/SpongeGame;
76: invokevirtual #99 // Method org/spongepowered/common/SpongeGame.getCommandManager:()Lorg/spongepowered/api/command/CommandManager;
79: invokeinterface #105, 1 // InterfaceMethod org/spongepowered/api/command/CommandManager.getCommands:()Ljava/util/Set;
84: invokeinterface #111, 1 // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
89: astore 6
91: aload 6
93: invokeinterface #117, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
98: ifeq 177
101: aload 6
103: invokeinterface #120, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
108: checkcast #122 // class org/spongepowered/api/command/CommandMapping
111: astore 7
113: aload 7
115: invokeinterface #126, 1 // InterfaceMethod org/spongepowered/api/command/CommandMapping.getCallable:()Lorg/spongepowered/api/command/CommandCallable;
120: instanceof #128 // class org/spongepowered/common/command/MinecraftCommandWrapper
123: ifeq 174
126: aload 7
128: invokeinterface #126, 1 // InterfaceMethod org/spongepowered/api/command/CommandMapping.getCallable:()Lorg/spongepowered/api/command/CommandCallable;
133: checkcast #128 // class org/spongepowered/common/command/MinecraftCommandWrapper
136: astore 8
138: aload_0
139: getfield #61 // Field data:Lorg/spongepowered/api/service/permission/MemorySubjectData;
142: getstatic #134 // Field org/spongepowered/api/service/permission/SubjectData.GLOBAL_CONTEXT:Ljava/util/Set;
145: aload 8
147: invokevirtual #138 // Method org/spongepowered/common/command/MinecraftCommandWrapper.getCommandPermission:()Ljava/lang/String;
150: aload 8
152: getfield #142 // Field org/spongepowered/common/command/MinecraftCommandWrapper.command:Lnet/minecraft/command/ICommand;
155: aload_1
156: invokeinterface #146, 1 // InterfaceMethod net/minecraft/command/ICommandSender.func_184102_h:()Lnet/minecraft/server/MinecraftServer;
161: aload_1
162: invokeinterface #152, 3 // InterfaceMethod net/minecraft/command/ICommand.func_184882_a:(Lnet/minecraft/server/MinecraftServer;Lnet/minecraft/command/ICommandSender;)Z
167: invokestatic #158 // Method org/spongepowered/api/util/Tristate.fromBoolean:(Z)Lorg/spongepowered/api/util/Tristate;
170: invokevirtual #162 // Method org/spongepowered/api/service/permission/MemorySubjectData.setPermission:(Ljava/util/Set;Ljava/lang/String;Lorg/spongepowered/api/util/Tristate;)Z
173: pop
174: goto 91
177: invokestatic #292 // Method java/lang/System.nanoTime:()J
180: lload 4
182: lsub
183: lstore_2
184: ldc_w #282 // String private void org.spongepowered.common.command.WrapperCommandSource#<init>(net.minecraft.command.ICommandSender)
187: lload_2
188: invokestatic #296 // Method $btrace$com$negafinity$btrace$Logger$exit:(Ljava/lang/String;J)V
191: return
LocalVariableTable:
Start Length Slot Name Signature
138 36 8 wrapper Lorg/spongepowered/common/command/MinecraftCommandWrapper;
113 61 7 command Lorg/spongepowered/api/command/CommandMapping;
0 192 0 this Lorg/spongepowered/common/command/WrapperCommandSource;
0 192 1 sender Lnet/minecraft/command/ICommandSender;
LineNumberTable:
line 56: 0
line 57: 17
line 58: 22
line 62: 52
line 63: 73
line 64: 113
line 65: 126
line 66: 138
line 67: 156
line 66: 170
line 69: 174
line 70: 177
MethodParameters:
Name Flags
sender
这并不容易看到,但是在通过文本差异工具运行两个输出之后,我发现了这个:
显然它正在删除整个 StackMapTable。我的问题是为什么/怎么会发生这种情况?
我对COMPUTE_MAXS
标志进行了研究,但我不确定这是否是预期的行为,或者这是因为其他原因而发生的。
谢谢!
更新:
我忘了把提醒我这个问题的错误:
java.lang.VerifyError: Expecting a stackmap frame at branch target 177
Exception Details:
Location:
org/spongepowered/common/command/WrapperCommandSource.<init>(Lnet/minecraft/command/ICommandSender;)V @98: ifeq
Reason:
Expected stackmap frame at this location.
Bytecode:
0x0000000: 2ab7 001a 1301 1ab8 011e 0941 b801 2437
0x0000010: 042a 2bb5 001c 2abb 001e 59b8 0024 b600
0x0000020: 2a12 2cb9 0032 0200 b600 38c0 002c b700
0x0000030: 3bb5 003d 2ab4 003d 2ab4 001c 59b6 0043
0x0000040: 57ba 0059 0000 b800 5fb8 0024 b600 63b9
0x0000050: 0069 0100 b900 6f01 003a 0619 06b9 0075
0x0000060: 0100 9900 4f19 06b9 0078 0100 c000 7a3a
0x0000070: 0719 07b9 007e 0100 c100 8099 0033 1907
0x0000080: b900 7e01 00c0 0080 3a08 2ab4 003d b200
0x0000090: 8619 08b6 008a 1908 b400 8e2b b900 9201
0x00000a0: 002b b900 9803 00b8 009e b600 a257 a7ff
0x00000b0: adb8 0124 1604 6541 1301 1a20 b801 28b1
0x00000c0: