java内部类文件名太长

发布于 2024-07-24 23:55:02 字数 360 浏览 4 评论 0原文

我正在将某种语言的程序翻译为嵌套的 java 类。 在某些时候,嵌套的级别变得如此之深,以至于我得到:

compiling Test.javaTest.java:5179: error whilewriting : Test$2...$1.class (File name too long)

where ... is a long string 。

我使用的是 ext3 文件系统,因此文件名长度仅限于 256 个字符。 另外,我想暂时继续使用这种翻译方法(到内部类),因为我更感兴趣的是测试执行闭包转换的语言,这将解决问题。 有没有一种快速而肮脏的方法来绕过这个? (也许使用不同的文件系统或告诉 javac 生成不同的文件名?)

I'm translating programs of some language to nested java classes. At some point, the level of nesting becomes so deep that I get:

compiling Test.javaTest.java:5179: error while writing : Test$2...$1.class (File name too long)

where ... is a long string.

I'm using an ext3 filesystem, so i'm limited to 256 character long filenames. Also, I would like to keep on with this translation method (to inner classes) for the moment because I'm more interested in testing the language that in performing closure conversion, which would solve the problem. Is there a quick'n dirty way to bypass this? (using a different filesystem or telling javac to generate different filenames maybe?)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

温柔女人霸气范 2024-07-31 23:55:03

底线是,是的,这是可行的。 人们可以更改内部类的名称,因此它比 javac 分配的原始名称短。

我正在搜索 Java 语言规范 和 < a href="http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html" rel="nofollow noreferrer">Java 虚拟机规范 查找它的位置讨论使用 $ 字符来表示内部类,但无法找到对其的引用。 原因是,这并不重要。

案例和要点:

class A {
    class B {
        class C {}
    }

    A() {
        new B().new C();
    }

    public static void main(String[] s){
        new A();
    }
}

在这里,我们有嵌套的内部类。 编译后,我们得到以下文件:

A.class
A$B.class
A$B$C.class

这是一个快速实验:

  1. 打开 A.class 文件,将引用更改为 A$B$C 并将其更改为 <代码>ABCDE。
  2. A$B$C.class 重命名为 ABCDE.class
  3. 打开ABCDE.class,并将引用更改为ABCDE
  4. 运行java A,看是否运行。

注意:A$B$C 更改为 ABCDE 的原因是标识符长度的变化似乎破坏了 class< /code> 文件格式,并且会导致错误。 技术解释将在本文末尾。

结果? 有用。

原因就在 class 文件中。 这是原始 A.class 的反汇编,仅显示相关部分:

Compiled from "A.java"
class A extends java.lang.Object
  SourceFile: "A.java"
  InnerClass: 
   #10= #3 of #7; //B=class A$B of class A
   #22= #2 of #3; //C=class A$B$C of class A$B

// ... snip ... //

const #2 = class    #21;    //  A$B$C

// ... snip ... //

const #21 = Asciz   A$B$C;

// ... snip ...//

原来,内部类的名称只是常量池中的名称。

如果A.class常量池中的A$B$C类的名称更改为ABCDE,并且如果A$B$Cclass'文件名和class文件中的名称被更改,那么Java虚拟机将很乐意执行新命名的内部类。

这是什么意思?

不需要使用 MyClass$1$1$1 ... $1 作为类名,但可以使用任何其他适合自己需要的名称,因此,可以在更短的文件中包含更多组合姓名。

有人会怎么做呢? 我将把它作为练习留给读者。

关于使用ABCDE作为新类名的注意事项

在这篇文章中,嵌套内部类的名称A$B$C是更改为ABCDE以保持类名的长度相同,以防止抛出ClassFormatError。 原因是 常量池的CONSTANT_Utf8_info结构有一个length属性,表示字符串的长度。 当我在文本编辑器中编辑 class 文件时,我无法更改长度。

为了缩短常量池中的字符串,我认为必须更改 length 字段的值以反映字符串本身的长度。

更新

是的,可以编辑class文件的常量池来缩短内部类的名称。

我能够将 ABCDE 类更改为 Z 类。

以下是 A.class 的反汇编部分:

Compiled from "A.java"
class A extends java.lang.Object
  SourceFile: "A.java"
  InnerClass: 
   #10= #3 of #7; //B=class A$B of class A
   #22= #2 of #3; //C=class Z of class A$B

// ... snip ...//

const #2 = class    #21;    //  Z

// ... snip ...//

const #21 = Asciz   Z;

// ... snip ...//

可以看出,内部类现在由 Z 引用,而不是 A$B$ C。

通过在 A.classA$B$C.class 文件中查找字符串 A$B$C 来执行更改,并将其替换为 Z,并将字符串前的字符从值 0x05 更改为 0x01,表示现在字符串的长度为1 而不是 5

通过这些更改,并将文件重命名为 Z.class,程序运行起来就像什么也没发生过一样。

所以,是的,也可以缩短内部类的名称。

Bottom line is, yes, it's doable. One can change the name of an inner class, so it is shorter than the original name assigned by javac.

I was searching through The Java Language Specification and The Java Virtual Machine Specification to find where it talks about using the $ character to denote an inner class, and was not able to find a reference to it. The reason is, it doesn't really matter.

Case and point:

class A {
    class B {
        class C {}
    }

    A() {
        new B().new C();
    }

    public static void main(String[] s){
        new A();
    }
}

Here, we have nested inner classes. When compiled, we get the following files:

A.class
A$B.class
A$B$C.class

Here's a quick experiment:

  1. Open the A.class file, and change the reference to A$B$C and change it to ABCDE.
  2. Rename the A$B$C.class to ABCDE.class.
  3. Open the ABCDE.class, and change the reference to ABCDE.
  4. Run java A, see if it runs.

Note: The reason the A$B$C was changed to ABCDE is because the change in the length of the identifier seems to mangle the class file format, and will cause an error. Technical explanation will be at the end of this post.

Result? It works.

The reason is in the class file. Here's a disassembly of the original A.class, and only the relevant parts:

Compiled from "A.java"
class A extends java.lang.Object
  SourceFile: "A.java"
  InnerClass: 
   #10= #3 of #7; //B=class A$B of class A
   #22= #2 of #3; //C=class A$B$C of class A$B

// ... snip ... //

const #2 = class    #21;    //  A$B$C

// ... snip ... //

const #21 = Asciz   A$B$C;

// ... snip ...//

Turns out, the name of the inner classes are just names in the constant pool.

If the name for the A$B$C class in the constant pool of A.class is changed to ABCDE, and if the A$B$C class' file name and name in the class file is changed, then the Java virtual machine will happy execute with the newly named inner class.

What does this mean?

One does not need to use the MyClass$1$1$1 ... $1 for the class name, but anything else that would suite one's need, therefore, it would be possible to have more combinations in a shorter file name.

How would someone go and do this? That I'll leave as an exercise to the reader.

Note on the use of ABCDE as the new class name

In this post, the name of the nested inner class, A$B$C was changed to ABCDE to keep the length of the class name the same, in order to prevent a ClassFormatError from being thrown. The reason for this is that the CONSTANT_Utf8_info structure of the constant pool has a length property which denotes the length of the string. I wasn't able to change the length as I was editing the class file in a text editor.

In order to shorten the string in the constant pool, I would presume that one would have to alter the value of the length field to reflect the length of the string itself.

Update

Yes, it is possible to edit the class file's constant pool to shorten the name of the inner class.

I was able to change the ABCDE class to Z class.

Here's a portion of the disassembly of the A.class:

Compiled from "A.java"
class A extends java.lang.Object
  SourceFile: "A.java"
  InnerClass: 
   #10= #3 of #7; //B=class A$B of class A
   #22= #2 of #3; //C=class Z of class A$B

// ... snip ...//

const #2 = class    #21;    //  Z

// ... snip ...//

const #21 = Asciz   Z;

// ... snip ...//

As can be seen, the inner class is now referred to by Z, rather than A$B$C.

The change was performed by looking for the string A$B$C in the A.class and A$B$C.class files, and replacing it with Z, and changing the character before the string from the value 0x05 to 0x01, denoting that the length of the string is now 1 rather than 5.

With those changes, along with renaming the file to Z.class, the program ran as if nothing ever happened.

So, yes, it is possible to shorten the name of the inner class as well.

寄风 2024-07-31 23:55:03

一种可能的解决方案是在另一个操作系统上进行编译,然后使用混淆器,例如 yGuard 。 默认情况下,混淆器会将类名更改为最小名称(例如 A、B、C...),从而大大缩短类名(以及文件名)。

不过,根据您想要测试的内容,可能对您没有用。

1 possible solution is to compile on another operating system, then use an Obfuscater such as yGuard. An obfuscater by default will change the class names to a minimal name (eg A, B, C...) thereby shortening the class name (and therefore file name) considerably.

Might be no use to you depending on exactly what you want to test, though.

去了角落 2024-07-31 23:55:03

您可以从 java 内部编译 java,将输出发送到您自己实现的文件管理器。

使用 javax.tools.JavaCompilerJavaFileManager 为您提供编译后的文件输出,您可以直接将其写入 jar 吗?

You can compile java from within java, sending the output to a file manager you implement yourself.

Use javax.tools.JavaCompiler, with a JavaFileManager that supplies you with the compiled output, which you could perhaps write directly to a jar?

美男兮 2024-07-31 23:55:03

文件系统比较,看起来 ResierFS 可能是唯一支持更长文件名的系统之一。 我会对这种方法保持警惕,因为所有工具(javac、java、ant、ls、rm、cp 等)可能会对文件名长度做出假设,因为大多数文件系统都是 255 并且您将被绑定到一个 FS (如果它消失了怎么办?)如果这纯粹是学术性的,请重新格式化(或使用虚拟化)。

您可能只需要重新评估您的算法以避免将类嵌套得太深。 可以使用多个文件吗? 我知道您不想这样做,但这可能是唯一选项

Comparison of File Systems, looks like ResierFS may be one of the only to support longer filenames. I'd be wary of this method as the all of the tools (javac, java, ant, ls, rm, cp, etc) might make assumptions about filename length since most filesystems are 255 and you'll be tied to one FS (what if it goes away?) If this is purely academic that reformat away (or use virtualization).

You may just need to reevalutate your algorithm to avoid nesting classes so deep. Can you use multiple files? I know you don't want to do it, but this may be the only option

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文