1

我正在使用开源库 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:
4

1 回答 1

0

很晚的答案,但希望对未来的读者在删除 StackMapTable 方面有用。

在我的公司,我们最近遇到了一个类似的问题,其中 MAX 值(特别是 max_stack)被错误地计算为一些检测方法,最终导致 JVM GC 崩溃。

最初作为 ASM 问题出现的问题,后来证明是 JVM 问题(仍有待确认)。
我们注意到,当类被重新转换(在已经加载后被检测)时,类字节丢失了 StackMapTable,而不是在第一次加载时检测类。

在 Java 7+ 上,ASM 将尝试根据现有的堆栈帧计算 MAX,始终假设它们在那里。当 StackMapTables 丢失时,ASM 可能会发出错误的 max_stack 值。

因此,可能不是 ASM 省略了 StackMapTables,而是 JVM 本身。

有趣的是,IBM J9 VM 上不会出现此问题 - 仅在 HotSpot (OpenJDK) 上。

于 2019-07-13T08:14:42.003 回答