질문:
- 질문 제목 : 클래스의 순서는 어떤 기준으로 정하나요??
- 질문 내용 : main메서드에 Time객체를 생성하고
참조변수 t를통해 시간을 호출하는데 Time 객체를 생성하는 클래스가 선언된곳보다 뒤에 있어도 상관 없는건가요??? 코드는 위에서 아래로 진행되는거 아닌가요???
public class Exe5 {
public static void main(String[] args) {
Time t = new Time();
t.hour = 12;
t.minute = 34;
t.second = 56;
System.out.println(t.hour);
System.out.println(t.minute);
System.out.println(t.second);
}
}
class Time {
int hour;
int minute;
int second;
}
일단 main 함수 기준으로 아래와 같이 짰다면 에러가 발생한다.
public class Exe5 {
public static void main(String[] args) {
t.hour = 12;
t.minute = 34;
t.second = 56;
Time t = new Time(); // 여기가 내려왔다.
System.out.println(t.hour);
System.out.println(t.minute);
System.out.println(t.second);
}
}
class Time {
int hour;
int minute;
int second;
}
error: cannot find symbol
t.hour = 12;
^
symbol: variable t
location: class Exe5
일단 질문대로 코드는 위에서 아래로 흐름을 갖는다.
그렇다면 Time 클래스의 경우 Exe5보다 아래에 있는데 컴파일러는 어떻게 해당 심볼(class)를 찾을 수 있을까?
(C언어의 경우 이런 경우에 에러가 난다. 그래서 타입의 선언을 호출하는 쪽 앞으로 미리 당기는 구문이 존재한다.)
만약 Time이 없는 채로 컴파일을 하게된다면?
아래와 같은 타입을 찾을 수 없는 에러가 발생한다.
Exe5.java:5: error: cannot find symbol
Time t = new Time();
^
symbol: class Time
location: class Exe5
Exe5.java:5: error: cannot find symbol
Time t = new Time();
^
symbol: class Time
location: class Exe5
2 errors
컴파일 하는 여러 단계 중 ATTR 단계에서 에러가 발생하게 된다. (아래에 나온다)
컴파일 상태에 따른 단계
javac 컴파일러의 경우 총 10가지의 상태를 가지는데 CompileStates 이라는 enum에 정의되어 있다.
package com.sun.tools.javac.comp;
public class CompileStates extends HashMap<Env<AttrContext>, CompileStates.CompileState> {
// ...
/** Ordered list of compiler phases for each compilation unit. */
public enum CompileState {
INIT(0),
PARSE(1),
ENTER(2),
PROCESS(3),
ATTR(4),
FLOW(5),
TRANSTYPES(6),
UNLAMBDA(7),
LOWER(8),
GENERATE(9);
// ...
이 상태는 실질적인 컴파일을 하는 JavaCompiler에 정의되어 현재 상태를 기록한다.
package com.sun.tools.javac.main;
public class JavaCompiler {
// ...
protected CompileStates compileStates;
// ...
PARSE
또한 컴파일러에 대한 지식이 있다면 소스코드를 토큰화하여 파싱트리를 구성하는 것을 알고 있을 것이다.
CompileStates.PARSE 에 해당하고 JavaCompiler의compile 메서드에서 수행한다. (아래코드에서 parseFiles부분이다)
processAnnotations(
enterTrees(
stopIfError(CompileState.PARSE,
initModules(stopIfError(CompileState.PARSE, parseFiles(sourceFileObjects))))
),
classnames
);
자바에서는 구문분석 트리를 JCTree (JavaCompilerTree)라는 타입으로 구성을 한다.
package com.sun.tools.javac.tree;
public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
// ..
public static class JCClassDecl extends JCStatement implements ClassTree
PROCESS
파싱이 끝났으면 컴파일 정책에 따라 다음 단계로 수행된다. 정책은 CompilePolicy 으로 5가지를 가진다.
package com.sun.tools.javac.main;
public class JavaCompiler {
// ...
protected static enum CompilePolicy {
ATTR_ONLY,
CHECK_ONLY,
SIMPLE,
BY_FILE,
BY_TODO;
// ...
ATTR_ONLY는 파스 트리의 속성만
CHECK_ONLY는 파스트리의 속성과 흐름 분석만
SIMPLE는 파스트리의 속성과 흐름 분석, 그리고 설탕물빼기(desugar) 그리고 결과물 생성까지 수행한다. 어떤 에러가 발생하게 되면 결과물을 만들지 않는다.
등의 단계별로 하는 일이 많아진다. 보통은 BY_TODO 정책으로 아래와 같이 모든 프로세스를 다 수행한다.
generate(desugar(flow(attribute(todo.remove()))));
ATTR
이 단계는 파스 트리에 속성(attribute)를 추가하며 주요 문맥 의존을 파악하는 단계이다.
package com.sun.tools.javac.comp;
public class Attr extends JCTree.Visitor {
// ..
public void attrib(Env<AttrContext> env) {
switch (env.tree.getTag()) {
case MODULEDEF:
attribModule(env.tree.pos(), ((JCModuleDecl)env.tree).sym);
break;
case TOPLEVEL:
attribTopLevel(env);
break;
case PACKAGEDEF:
attribPackage(env.tree.pos(), ((JCPackageDecl) env.tree).packge);
break;
default:
attribClass(env.tree.pos(), env.enclClass.sym);
}
}
바로 위에서 이야기 했던 Time 클래스가 컴파일시 없을 경우 이 단계에서 에러가 발생하게 된다.
package com.sun.tools.javac.tree;
public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
// ..
final Types types;
private Type capture(Type type) {
return types.capture(type);
}
// ...
package com.sun.tools.javac.code;
public class Types {
protected static final Context.Key<Types> typesKey = new Context.Key<>();
final Symtab syms;
FLOW
이 단계는 ATTR가 수행된 파스 트리의 데이터 흐름에 대한 체크를 수행한다. analyzeTree가 호출된다.
package com.sun.tools.javac.comp;
public class Flow {
// ...
public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
new AliveAnalyzer().analyzeTree(env, make);
new AssignAnalyzer().analyzeTree(env);
new FlowAnalyzer().analyzeTree(env, make);
new CaptureAnalyzer().analyzeTree(env, make);
}
UNLAMBDA, LOWER
단계 이름은 desugar이지만 상태는 UNLAMBDA나 LOWER로 설정될 수 있다.
GENERATE
마지막 단계로 코드를 생성하고 class파일로 기록하는 역할을 한다.
JNI 코드가 있다면 JNIWriter가 개입할 수 있으나 아니라면 ClassWriter가 사용된다.
package com.sun.tools.javac.jvm;
public class ClassWriter extends ClassFile {
// ..
public JavaFileObject writeClass(ClassSymbol c)
throws IOException, PoolOverflow, StringOverflow
// ..
public void writeClassFile(OutputStream out, ClassSymbol c)
throws IOException, PoolOverflow, StringOverflow {
'Programing > JVM(Java, Kotlin)' 카테고리의 다른 글
[Java] Stream in depth : Stream & Pipeline (0) | 2019.10.27 |
---|---|
[Sonarqube] Functional Interfaces should be as specialised as possible (0) | 2019.10.18 |
성지순례와 인터페이스 (0) | 2019.10.08 |
[Java] Wrapper 클래스 vs Overload 메서드 중 우선순위는? (1) | 2019.10.06 |
[Java] forEach와 for each 의 차이점은? (5) | 2019.10.05 |