JVMS21 공식문서 정리 - chapter4 - (1)

응큼한포도·2024년 4월 16일

JVMS21 공식문서 정리

목록 보기
10/11
post-thumbnail

프로젝트 들어가야 해서 아마 늦게 올라갈듯 ㅎㅎ ㅈㅅ;;

The class File Format

이 장은 Java Virtual Machine의 클래스 파일 형식을 설명한다. 각 클래스 파일은 단일 클래스, 인터페이스 또는 모듈의 정의를 포함한다. 클래스, 인터페이스 또는 모듈이 파일에 명시적으로 포함되어 있지 않아도 된다(예: 클래스로더에 의해 생성된 클래스 때문에). 하지만 우리는 클래스, 인터페이스 또는 모듈의 유효한 표현을 일반적으로 클래스 파일 형식으로 지칭할 것이다.

클래스 파일은 8비트 바이트의 스트림으로 구성된다. 16비트 및 32비트 값은 각각 연속된 두 개 및 네 개의 8비트 바이트를 읽어서 생성된다. 다중바이트 데이터 항목은 항상 빅엔디안 순서로 저장되며 높은 바이트가 먼저 나온다. 이 장에서는 부호 없는 한 바이트, 두 바이트 또는 네 바이트의 양을 나타내기 위해 데이터 형식 u1, u2 및 u4를 정의한다.

이 장에서도 앞과 같이 C 언어와 비슷한 구조 표기법을 사용하여 클래스 파일 형식을 의사구조로 표현한다. 클래스 및 클래스 인스턴스의 필드와 혼동을 피하기 위해, 클래스 파일 형식을 설명하는 구조체의 내용을 "item"으로 언급한다. 연이은 항목들은 패딩이나 정렬 없이 순차적으로 클래스 파일에 저장된다.

여러 클래스 파일 구조에서는 크기가 다른 항목들로 이루어진 테이블을 사용하겠다. 테이블 항목을 가리키기 위해 C 언어와 유사한 배열 구문을 사용하지만, 테이블이 크기가 다른 구조의 스트림임을 고려해 테이블 인덱스를 직접적으로 바이트 오프셋으로 변환하는 것은 불가능하다.

데이터 구조를 배열로 언급하는 경우, 이는 크기가 고정된 연속된 항목으로 이루어진 것이며, 배열처럼 인덱싱할 수 있다.

이 장에서 ASCII 문자에 대한 참조는 ASCII 문자에 해당하는 Unicode 코드 포인트로 해석되어야 한다.

The ClassFile Structure

하나의 클래스 파일 구조엔 여러개의 클래스 파일들이 들어있다.

ClassFile {
  u4 magic;
  u2 minor_version;
  u2 major_version;
  u2 constant_pool_count;
  cp_info constant_pool[constant_pool_count-1];
  u2 access_flags;
  u2 this_class;
  u2 super_class;
  u2 interfaces_count;
  u2 interfaces[interfaces_count];
  u2 fields_count;
  field_info fields[fields_count];
  u2 methods_count;
  method_info methods[methods_count];
  u2 attributes_count;
  attribute_info attributes[attributes_count];
}

각각의 줄을 살펴보자.

Magic
magic 항목은 클래스 파일 형식을 식별하는 매직 넘버를 제공한다. 이 값은 0xCAFEBABE이다.

minor_version, major_version
minor_version 및 major_version 항목의 값은 이 클래스 파일의 마이너 및 메이저 버전 번호다. 메이저와 마이너 버전 번호는 클래스 파일 형식의 버전을 결정한다. 만약 클래스 파일이 메이저 버전 번호 M과 마이너 버전 번호 m을 가지면, 해당 클래스 파일 형식의 버전은 M.m으로 표시된다.
Java SE N을 준수하는 Java 가상 머신 구현은 표 4.1-A의 네 번째 열인 "지원되는 메이저 버전"에 지정된 클래스 파일 형식의 메이저 버전을 정확히 지원해야 한다. A .. B 표기법은 A부터 B까지의 메이저 버전을 의미하며, A와 B를 모두 포함합니다. "메이저"라는 세 번째 열은 각 Java SE 릴리스에서 도입된 메이저 버전을 보여준다. 즉, 해당 메이저 버전 항목을 포함하는 클래스 파일을 수용할 수 있었던 첫 번째 릴리스이다. 매우 초기 릴리스의 경우 Java SE 릴리스 대신 JDK 버전이 표시된다.

Java SE 플랫폼은 미리보기 기능을 정의할 수 있다. Java SE N (N ≥ 12)을 준수하는 Java 가상 머신 구현은 Java SE N의 모든 미리보기 기능을 지원해야 하며, 다른 Java SE 릴리스의 미리보기 기능은 지원하지 않아야 한다. 구현은 기본적으로 지원되는 미리보기 기능을 비활성화해야 하며, 모든 미리보기 기능을 활성화할 수 있는 방법을 제공해야 하지만, 일부만 활성화할 수 있는 방법을 제공해서는 안 된다.

constant_pool_count

constant_pool_count 항목의 값은 constant_pool 테이블의 항목 수에 1을 더한 값과 같다. constant_pool 인덱스는 0보다 크고 constant_pool_count보다 작으면 유효하다. 단, §4.4.5에서 언급된 long 및 double 유형의 상수에 대한 예외가 있다.

constant_pool[]
constant_pool은 클래스 파일 구조 및 그 하위 구조 내에서 참조되는 다양한 문자열 상수, 클래스 및 인터페이스 이름, 필드 이름 및 기타 상수를 나타내는 구조체(§4.4)의 테이블이다. 각 constant_pool 테이블 항목의 형식은 첫 번째 "tag" 바이트에 의해 나타내어진다.

constant_pool 테이블은 1부터 constant_pool_count - 1까지 인덱싱된다.

access_flags
access_flags 항목의 값은 이 클래스 또는 인터페이스의 액세스 권한과 속성을 나타내는 플래그의 마스크이다. 각 플래그가 설정되었을 때의 해석은 표 4.1-B에 지정되어 있다.

ACC_MODULE 플래그는 이 클래스 파일이 클래스나 인터페이스가 아닌 모듈을 정의한다는 것을 나타낸다. ACC_MODULE 플래그가 설정되어 있다면, 이 섹션의 끝에 제공된 특별한 규칙이 해당 클래스 파일에 적용된다. ACC_MODULE 플래그가 설정되어 있지 않으면, 현재 단락 아래에 바로 규칙이 적용된다.

인터페이스는 ACC_INTERFACE 플래그가 설정되어 있는 것으로 식별된다. ACC_INTERFACE 플래그가 설정되어 있지 않으면, 이 클래스 파일은 인터페이스나 모듈이 아닌 클래스를 정의한다.

만약 ACC_INTERFACE 플래그가 설정되어 있다면, ACC_ABSTRACT 플래그도 반드시 설정되어야 하며, ACC_FINAL, ACC_SUPER, ACC_ENUM 및 ACC_MODULE 플래그는 설정되어서는 안 된다.

만약 ACC_INTERFACE 플래그가 설정되어 있지 않다면, 표 4.1-B의 다른 플래그 중 어느 것이든 설정될 수 있다. 단, ACC_ANNOTATION 및 ACC_MODULE은 제외된다. 그러나 이러한 클래스 파일은 ACC_FINAL 및 ACC_ABSTRACT 플래그가 모두 설정되어 있어서는 안 된다. (JLS §8.1.1.2)

ACC_SUPER 플래그는 이 클래스나 인터페이스에 나타나는 invokespecial 명령어(§invokespecial)의 두 가지 대안적 의미 중 어느 것을 표현할 것인지 나타낸다. JVM의 명령어 세트에 대한 컴파일러는 ACC_SUPER 플래그를 설정해야 한다. Java SE 8 이상에서는 Java 가상 머신이 ACC_SUPER 플래그를 모든 클래스 파일에 설정된 것으로 간주한다. 이때 클래스 파일의 실제 플래그 값과 클래스 파일의 버전에 관계없이 ACC_SUPER 플래그가 설정되어 있는 것이다.

this_class

this_class는 상수 풀 테이블에서 유효한 인덱스여야 한다. 그 인덱스에 있는 constant pool 항목은 이 클래스 파일에서 정의된 클래스나 인터페이스를 나타내는 CONSTANT_Class_info 구조체여야 합니다.

super_class

클래스의 super_class 항목 값은 0이거나 constanct pool 테이블에서 유효한 인덱스여야 한다. 만약 super_class 항목 값이 0이 아니라면, 해당 인덱스에 있는 constant pool 항목은 이 클래스 파일에서 정의된 클래스의 직접적인 슈퍼클래스를 나타내는 CONSTANT_Class_info 구조체여야 한다. 슈퍼클래스나 그 어떤 슈퍼클래스도 해당 클래스 파일의 ClassFile 구조체의 access_flags 항목에서 ACC_FINAL 플래그가 설정되어 있어서는 안된다.

만약 super_class 항목 값이 0이라면, 해당 클래스 파일은 직접적인 슈퍼클래스가 없는 유일한 클래스 또는 인터페이스인 Object 클래스를 나타내야 한다. 인터페이스의 경우, super_class 항목 값은 항상 constant pool 테이블에서 유효한 인덱스여야 한다. 해당 인덱스에 있는 상수 풀 항목은 Object 클래스를 나타내는 CONSTANT_Class_info 구조체여야 한다.

interfaces_count

interfaces_count 항목의 값은 이 클래스나 인터페이스 타입의 직접적인 상위 인터페이스의 수를 나타낸다.

interfaces[]

interfaces 배열의 각 값은 상수 풀 테이블에서 유효한 인덱스여야 한다. interfaces[i]의 각 값(여기서 0 ≤ i < interfaces_count)에 있는 constant pool 항목은 이 클래스나 인터페이스 타입의 직접적인 상위 인터페이스를 나타내는 CONSTANT_Class_info 구조체여야 한다. 이 순서는 해당 타입의 소스에서 제공된 순서에 따라야 한다.

fields_count

fields_count 항목의 값은 fields 테이블에 있는 field_info 구조체의 수를 나타낸다. 이 field_info 구조체들은 이 클래스나 인터페이스 타입에서 선언된 모든 필드, 클래스 변수와 인스턴스 변수 모두를 나타낸다.

fields[]

fields 테이블의 각 값은 이 클래스나 인터페이스에서 선언된 필드의 완전한 설명을 제공하는 field_info 구조체여야 한다. fields 테이블에는 이 클래스나 인터페이스에서 선언된 필드만 포함된다. 슈퍼클래스나 슈퍼인터페이스에서 상속된 필드를 나타내는 항목은 포함되지 않는다.

methods_count

methods_count 항목의 값은 methods 테이블에 있는 method_info 구조체의 수를 나타낸다.

methods[]

methods 테이블의 각 값은 이 클래스나 인터페이스에 있는 메소드의 완전한 설명을 제공하는 method_info 구조체여야 한다. 만약 method_info 구조체의 access_flags 항목에서 ACC_NATIVE와 ACC_ABSTRACT 플래그가 모두 설정되어 있지 않다면, 해당 메소드를 구현하는 JVM 명령어도 제공되어야 한다.

method_info 구조체들은 이 클래스나 인터페이스 타입에서 선언된 모든 메소드를 나타낸다. 이는 인스턴스 메소드, 클래스 메소드, 인스턴스 초기화 메소드, 그리고 클래스나 인터페이스 초기화 메소드를 포함한다. methods 테이블에는 슈퍼클래스나 슈퍼인터페이스에서 상속된 메소드를 나타내는 항목이 포함되지 않는다.

attributes_count

attributes_count 항목의 값은 이 클래스의 속성 테이블에 있는 속성의 수를 나타낸다.

attributes[]

ClassFile 구조의 attributes 테이블에 나타나는 속성은 §4.7에 나열된 속성_info 구조여야 한다. 클래스 파일 구조의 attributes 테이블에 나타나는 속성에 대한 규칙은 §4.7에 있다. ClassFile 구조의 attributes 테이블에 있는 사전 정의되지 않은 속성에 대한 규칙은 §4.7.1에 있다.

access_flags 항목에서 ACC_MODULE 플래그가 설정되어 있으면 다른 플래그는 없어야 한다. 그리고 다음 규칙이 ClassFile 구조의 나머지 부분에 적용된다:

• major_version, minor_version: ≥ 53.0 (i.e., Java SE 9 and above)
• this_class: module-info
• super_class, interfaces_count, fields_count, methods_count: zero
• attributes: 하나의 module 속성은 꼭 있어야 한다. Except
for Module, ModulePackages, ModuleMainClass, InnerClasses,
SourceFile, SourceDebugExtension, RuntimeVisibleAnnotations, and
RuntimeInvisibleAnnotations을 제외한 사전 정의된 속성(§4.7)은 나타날 수 없다.

Names

Binary Class and Interface Names

클래스 및 인터페이스 이름은 항상 binary names으로 표시된다 (JLS §13.1).
ex) java.lang.String 와 같이 패키지, 클래스, 인터페이스를 점으로 구분하는 걸 binary names라고 한다.

이러한 이름은 항상 CONSTANT_Utf8_info 구조체(§4.4.7)로 표시되므로, 뭔 제약이 없으면 unicode에 있는 아무 언어나 가져와서 이름으로 써도 된다. 클래스 및 인터페이스 이름은 해당 이름이 설명자(§4.3)의 일부로 있는 CONSTANT_NameAndType_info 구조체(§4.4.6) 및 모든 CONSTANT_Class_info 구조체(§4.4.1)에서 참조된다.

역사적으로 클래스 파일 구조에 나타나는 binary names는 JLS §13.1에 문서화된 binary names과 다르다. 이 내부 형식에서는 일반적으로 이진 이름을 구성하는 식별자를 구분하는 ASCII 점(.)이 ASCII 슬래시(/)로 대체된다. 식별자 자체는 미정격 이름(§4.2.2)이어야 한다.

ex) com.example.MyClass 같은 경우는 클래스 파일 구조에선 com/example/MyClass /를 이용해서 표현된다.

Unqualified Names

메소드, 필드, 로컬 변수 및 형식 매개변수의 이름은 미확인 이름으로 저장된다. 미확인 이름은 최소한 하나의 유니코드 코드 포인트를 포함해야 하며, 아래 ASCII 문자들 중 어느 하나도 포함해서는 안된다: . ; [ / (즉, 점 또는 세미콜론 또는 왼쪽 대괄호 또는 슬래시).
메소드 이름은 특별한 메소드 이름 및 을 제외하고 ASCII 문자 < 또는 > (즉, 왼쪽 화살표 또는 오른쪽 화살표)를 포함해서는 안된다.

Module and Package Names

모듈 속성에서 참조된 모듈 이름은 상수 풀의 CONSTANT_Module_info 구조체에 저장된다(§4.4.11). CONSTANT_Module_info 구조체는 모듈 이름을 나타내는 CONSTANT_Utf8_info 구조체를 감싸며, 모듈 이름은 클래스 및 인터페이스 이름과 같이 "내부 형식"으로 인코딩되지 않는다. 즉, 모듈 이름에서 식별자를 구분하는 ASCII 점(.)은 ASCII 슬래시(/)로 대체되지 않는다.

모듈 이름은 다음 제약 조건에 따라 전체 유니코드 코드 공간에서 선택할 수 있다:

모듈 이름은 '\u0000'에서 '\u001F' (포함) 범위의 코드 포인트를 포함해서는 안된다.
ASCII 백슬래시()는 모듈 이름에서 이스케이프 문자로 예약되어 있다. 모듈 이름에 나타나는 경우, ASCII 백슬래시 뒤에 ASCII 백슬래시, ASCII 콜론(:), 또는 ASCII at-sign(@)이 바로 뒤에 올 때를 제외하고는 나타나서는 안된다. ASCII 문자열 \은 모듈 이름에서 백슬래시를 인코딩하는 데 사용될 수 있다.

ASCII 콜론(:)과 at 기호(@)는 모듈 이름에서 예약어다. 이러한 문자는 이스케이프되지 않은 경우 모듈 이름에 쓰면 안된다. 모듈 이름에 콜론 및 at 기호를 인코딩하기 위해 ASCII 문자 시퀀스 : 및 @을 사용할 수 있다.

Module 속성에서 참조된 패키지 이름은 상수 풀(§4.4.12)의 CONSTANT_Package_info 구조에 저장된다. CONSTANT_Package_info 구조는 내부 형식으로 인코딩된 패키지 이름을 나타내는 CONSTANT_Utf8_info 구조를 래핑한다.

Descriptors

Descriptors는 필드나 메서드의 타입을 나타내는 문자열이다. Descriptors는 수정된 UTF-8 문자열(§4.4.7)을 사용하여 클래스 파일 형식으로 표현되므로, 추가로 제약이 없는 경우 전체 Unicode 코드 공간에서 가져올 수 있다.

Grammar Notation

Descriptors는 grammar을 사용하여 지정된다. 이 grammar은 문자열 시퀀스가 다양한 유형의 문법적으로 올바른 Descriptors를 형성하는 방식을 설명하는 규칙의 집합이다. grammar의 단말 기호는 고정폭 글꼴로 표시된다.

Nonterminal 기호는 이탤릭체로 표시된다. Nonterminal의 정의는 정의되는 Nonterminal의 이름 다음에 콜론이 붙어서 도입된다.

그런 다음 Nonterminal에 대한 하나 이상의 대안적인 정의가 온다. 제작에서 오른쪽에 있는 {x} 구문은 x의 0 이상의 발생을 나타낸다. 규칙의 오른쪽에 있는 (one of) 구문은 다음 줄 또는 다음 줄의 각 단말 기호가 대체적인 정의임을 나타낸다.

Field Descriptors

Field Descriptors는 클래스, 인스턴스 또는 지역 변수의 타입을 나타낸다. Field Descriptors는 다음과 같은 구조를 가진다:

FieldDescriptor:
  FieldType:
      BaseType
      ObjectType
      ArrayType
  BaseType:
      (one of)
      B C D F I J S Z
      ObjectType:
      L ClassName ;
  ArrayType:
      [ ComponentType

  ComponentType:
      FieldType

기본 형 BaseType의 문자, ObjectType의 L 및 ; 그리고 ArrayType의 [는 모두 ASCII 문자다. ClassName은 내부 형식(§4.2.1)으로 인코딩된 이진 클래스 또는 인터페이스 이름을 나타낸다. FieldDescriptor를 유형으로 해석하는 방법은 Table 4.3-A에 나와 있다. 배열 유형을 나타내는 FieldDescriptor는 255개 이하의 차원을 가진 유형을 나타내는 경우에만 유효하다.

int 타입의 인스턴스 변수의 필드 서술자는 I다.
Object 타입의 인스턴스 변수의 필드 서술자는 Ljava/lang/Object;다. 여기서 클래스 Object의 binary names의 내부 형식이 사용된다.
double[][] 타입의 다차원 배열 인스턴스 변수의 필드 서술자는 [[[D다.

Method Descriptors

Method Descriptors는 메서드가 받는 매개변수의 타입을 나타내는 하나 이상의 Method Descriptors와, 메서드가 반환하는 값의 타입을 나타내는 return descriptor를 포함한다.

MethodDescriptor:
  ( {ParameterDescriptor} ) ReturnDescriptor
ParameterDescriptor:
  FieldType
  
ReturnDescriptor:
  FieldType
  VoidDescriptor
  
VoidDescriptor:
	V

문자 V는 메서드가 값을 반환하지 않음을 나타낸다 (즉, 결과가 void인 경우)

Method Descriptors는 다음과 같다:

Object m(int i, double d, Thread t) {...}

이건

(IDLjava/lang/Thread;)Ljava/lang/Object;

Thread와 Object의 binary names의 내부 형식이 사용되었음에 주의

Method Descriptors는 메서드 매개변수의 총 길이가 255 이하일 때에만 유효하다. 이 길이에는 인스턴스나 인터페이스 메서드 호출의 경우 this의 기여도도 포함된다. 총 길이는 개별 매개변수의 기여도를 합하여 계산되며, long 또는 double 타입의 매개변수는 길이에 두 개의 단위를, 다른 타입의 매개변수는 한 개의 단위를 부여한다.

Method Descriptors는 해당 메서드가 클래스 메서드인지 인스턴스 메서드인지에 관계없이 동일하다. 인스턴스 메서드는 의도된 인수에 추가로 메서드가 호출되는 객체에 대한 참조인 this를 전달받지만, 이 사실은 메서드 서술자에 반영되지 않는다. this에 대한 참조는 인스턴스 메서드를 호출하는 자바 가상 머신 명령에 의해 내부적으로 전달된다 (§2.6.1, §4.11).

0개의 댓글