4. GOTO Statement –Objective
• Syntax:
goto identifier;
• Runtime
– Program control moves to target statement
• Other Semantics
– Target (statement label) must be defined in same
scope, compilation error if not
– Potential circular gotos should give compilation
warning.
5. GOTO Statement – Means
1. OpenJDK
– Open source Java implementation
– Compiler implemented in Java
2. Modify compiler source to support goto
statements
6. Success case
public class GotoSuccess {
public static void main(String[] args) {
one: System.out.print("goto ");
two: System.out.print("Java");
goto four;
three: System.out.print("2014");
goto five;
four: System.out.print("Zone ");
goto three;
five: System.out.print("!");
}
}
10. Parse Enter Process Attribute Flow Desugar Generate
processAnnotations(enterTrees(…
parseFiles(…))),…)
…
generate(desugar(flow(attribute(…))))
11. Parse Enter Process Attribute Flow Desugar Generate
processAnnotations(enterTrees(…
parseFiles(…))),…)
…
generate(desugar(flow(attribute(…))))
12. Parse Enter Process Attribute Flow Desugar Generate
• Syntax: goto identifier;
• First convert character stream to token
stream (Scanner.java)
[g][o][t][o][ ][f][o][u][r][;]
GOTO IDENTIFIER SEMI
13. Parse Enter Process Attribute Flow Desugar Generate
• goto is already a reserved word in Java!
• Lucky for us, goto is therefore defined as a
TokenKind.
• Tokens.java:141: GOTO("goto")
• The scanner therefore works out of the box!
14. Parse Enter Process Attribute Flow Desugar Generate
final Scanner scanner=factory.newScanner(”goto identifier;”, false);
...
import com.sun.tools.javac.parser.Scanner;
...
15. Parse Enter Process Attribute Flow Desugar Generate
final Scanner scanner=factory.newScanner(”goto identifier;”, false);
scanner.nextToken();
16. Parse Enter Process Attribute Flow Desugar Generate
final Scanner scanner=factory.newScanner(”goto identifier;”, false);
scanner.nextToken();
assertThat(”First token is GOTO”, scanner.token().kind, is(GOTO));
17. Parse Enter Process Attribute Flow Desugar Generate
final Scanner scanner=factory.newScanner(”goto identifier;”, false);
scanner.nextToken();
assertThat(”First token is GOTO”, scanner.token().kind, is(GOTO));
scanner.nextToken();
18. Parse Enter Process Attribute Flow Desugar Generate
final Scanner scanner=factory.newScanner(”goto identifier;”, false);
scanner.nextToken();
assertThat(”First token is GOTO”, scanner.token().kind, is(GOTO));
scanner.nextToken();
assertThat("Second token is IDENTIFIER", scanner.token().kind,
is(IDENTIFIER));
19. Parse Enter Process Attribute Flow Desugar Generate
final Scanner scanner=factory.newScanner(”goto identifier;”, false);
scanner.nextToken();
assertThat(”First token is GOTO”, scanner.token().kind, is(GOTO));
scanner.nextToken();
assertThat("Second token is IDENTIFIER", scanner.token().kind,
is(IDENTIFIER));
scanner.nextToken();
20. Parse Enter Process Attribute Flow Desugar Generate
final Scanner scanner=factory.newScanner(”goto identifier;”, false);
scanner.nextToken();
assertThat(”First token is GOTO”, scanner.token().kind, is(GOTO));
scanner.nextToken();
assertThat("Second token is IDENTIFIER", scanner.token().kind,
is(IDENTIFIER));
scanner.nextToken();
assertThat("Third token is SEMI", scanner.token().kind, is(SEMI));
21. Parse Enter Process Attribute Flow Desugar Generate Abstract Syntax
CompilationUnit
ClassDefinition
FieldDefintion
MethodDefinition
Signature
Body
IfStatement
Goto
Wikipedia: “the visitor design pattern
is a way of separating an algorithm
from an object structure on which it
operates”
visitClassDef(..)
visitMethodDef(
..)
visitIf(..)
Tree
GOTO IDENTIFIER SEMI
[g][o][t][o][ ][f][o][u][r][;]
23. Parse Enter Process Attribute Flow Desugar Generate
• Interface based visitors
24. Parse Enter Process Attribute Flow Desugar Generate
• Class based visitors
public void visitGoto(JCGoto tree) {
try {
print("goto " + tree.label + ";");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void visitGoto(JCGoto tree) {
//TODO implement
}
25. Parse Enter Process Attribute Flow Desugar Generate
public class GotoResolver {
Map<GotoTree, Name> gotos;
Map<Name, LabeledStatementTree> targets;
List<StatementTree> statementsInSequence;
...
}
public static class JCLabeledStatement extends JCStatement
implements LabeledStatementTree {
…
public GotoResolver handler;
…
}
26. Parse Enter Process Attribute Flow Desugar Generate
TreeMaker.java:
public JCGoto Goto(Name label, GotoResolver resolver) {
JCGoto tree = new JCGoto(label, resolver);
tree.pos = pos;
return tree;
}
• JavacParser.parseStatement()
case GOTO: {
nextToken();
Name label = ident();
JCGoto t = to(F.at(pos).Goto(label, getGotoResolver()));
accept(SEMI);
return t;
}
27. Parse Enter Process Attribute Flow Desugar Generate
processAnnotations(enterTrees(…
parseFiles(…))),…)
…
generate(desugar(flow(attribute(…))))
28. Parse Enter Process Attribute Flow Desugar Generate
• Basic sanity testing of compilation unit
– File name and folder location
– Duplicate class names
• Corrections
– Add default constructor if no constructors are
declared
29. Parse Enter Process Attribute Flow Desugar Generate
• Default constructor
public class SimpleClass {
}
30. Parse Enter Process Attribute Flow Desugar Generate
• Default constructor
public class SimpleClass {
public SimpleClass() {
super();
}
}
31. Parse Enter Process Attribute Flow Desugar Generate
processAnnotations(enterTrees(…
parseFiles(…))),…)
…
generate(desugar(flow(attribute(…))))
32. Parse Enter Process Attribute Flow Desugar Generate
• Annotation processing API
• Part of ordinary javac process since Java 1.6
• Plugin API (see javac documentation)
33. Parse Enter Process Attribute Flow Desugar Generate
• Output controlled by command line switches
@
- proc:none – do not process annotations
- proc:only – only process annotations, do not compile
34. Parse Enter Process Attribute Flow Desugar Generate
processAnnotations(enterTrees(…
parseFiles(…))),…)
…
generate(desugar(flow(attribute(…))))
35. Parse Enter Process Attribute Flow Desugar Generate
• Semantic checks
– Types
– References
• Corrections
– Add required calls to super constructor
36. Parse Enter Process Attribute Flow Desugar Generate
• We want to verify that goto target exists in
current scope
public class GotoMissingLabel {
public static void main(String[] args) {
one: System.out.print("goto ");
two: System.out.print("Java");
goto six;
three: System.out.print("2014");
goto five;
four: System.out.print("Zone ");
goto three;
five: System.out.print("!");
}
}
37. Parse Enter Process Attribute Flow Desugar Generate
• Attr.java:
@Override
public void visitGoto(JCGoto that) {
that.findTarget();
if(that.target==null)
log.error(that.pos(), "undef.label", that.label);
result = null;
}
class JCGoto:
…
public void findTarget() {
this.target = (JCLabeledStatement)this.handler.findTarget(this);
}
38. Parse Enter Process Attribute Flow Desugar Generate
processAnnotations(enterTrees(…
parseFiles(…))),…)
…
generate(desugar(flow(attribute(…))))
39. Parse Enter Process Attribute Flow Desugar Generate
Flow analysis
• Detect unreachable code
• Verify assignments
• Ensure proper method return
• Verify (effectively) final
• Check exception flow
40. Parse Enter Process Attribute Flow Desugar Generate
We want to detect circular gotos:
public class GotoCircularWarning {
public static void main(String[] args) {
one: System.out.print("goto ");
two: System.out.print("Java");
goto four;
three: System.out.print("2014");
goto five;
four: System.out.print("Zone ");
goto three;
five: System.out.println("!");
goto one;//forms infinite loop
}
}
41. Parse Enter Process Attribute Flow Desugar Generate
Flow.FlowAnalyzer class:
@Override
public void visitGoto(JCGoto tree) {
if (tree.handler.detectCircularGotoPosition(tree))
log.warning(tree.pos, "circular.goto");
}
compiler.properties:
...
compiler.warn.circular.goto=circular goto
...
42. Parse Enter Process Attribute Flow Desugar Generate
processAnnotations(enterTrees(…
parseFiles(…))),…)
…
generate(desugar(flow(attribute(…))))
43. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Erase generic types
public class Bridge implements Comparator {
}
44. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Erase generic types
public class Bridge implements Comparator {
}
public interface Comparator<T> {t
int compare(T o1, T o2);
}or<T> {
45. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Erase generic types
public class Bridge implements Comparator<Integer> {
}
public interface Comparator<T> {t
int compare(T o1, T o2);
}or<T> {
46. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Erase generic types
public class Bridge implements Comparator<Integer> {
public int compare(Integer first, Integer second) {
return first - second;
}
}
public interface Comparator<T> {t
int compare(T o1, T o2);
}or<T> {
47. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Erase generic types
public class Bridge implements Comparator {
public int compare(Integer first, Integer second) {
return first - second;
}
}
48. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Erase generic types
public class Bridge implements Comparator {
public int compare(Integer first, Integer second) {
return first - second;
}
/*synthetic*/
public int compare(Object first, Object second) {
return this.compare((Integer)first, (Integer)second);
}
}
49. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Extract Inner Class
public class Outer {
private void foo() { }
public Runnable fooRunner() {
return new Runnable() {
public void run() {
foo();
}
};
}
}
50. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Extract Inner Class
public class Outer {
private void foo() { }
public Runnable fooRunner() {
return new Runnable() {
public void run() {
foo();
}
};
}
}
51. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Extract Inner Class
public class Outer {
private void foo() { }
public Runnable fooRunner() {
return new Outer$1(this);
}
}
class Outer$1 implements Runnable {
final Outer this$0;
public void run() {
this$0.foo();
}
}
52. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Extract Inner Class
public class Outer {
private void foo() { }
public Runnable fooRunner() {
return new Outer$1(this);
}
}
class Outer$1 implements Runnable {
final Outer this$0;
Outer$1(final Outer this$0) {
this.this$0 = this$0;
super();
}
public void run() {
this$0.foo();
}
}
53. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Extract Inner Class
public class Outer {
private void foo() { }
public Runnable fooRunner() {
return new Outer$1(this);
}
}
class Outer$1 implements Runnable {
final Outer this$0;
Outer$1(final Outer this$0) {
this.this$0 = this$0;
super();
}
public void run() {
this$0.foo();
}
}
54. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Extract Inner Class
public class Outer {
private void foo() { }
public Runnable fooRunner() {
return new Outer$1(this);
}
static void access$000(
Outer x0) {
x0.foo();
}
}
class Outer$1 implements Runnable {
final Outer this$0;
Outer$1(final Outer this$0) {
this.this$0 = this$0;
super();
}
public void run() {
Outer.access$000(this$0);
}
}
55. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Boxing
List<Integer> list =
Arrays.asList(1, 2);
for (Integer i : list) {
System.out.println("Double: " + i * 2);
}
56. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Boxing
List<Integer> list =
Arrays.asList(1, 2);
for (Integer i : list) {
System.out.println("Double: " + i * 2);
}
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
57. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Boxing
List<Integer> list =
Arrays.asList(Integer.valueOf(1), Integer.valueOf(2));
for (Integer i : list) {
System.out.println("Double: " + i * 2);
}
58. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Unboxing
List<Integer> list =
Arrays.asList(Integer.valueOf(1), Integer.valueOf(2));
for (Integer i : list) {
System.out.println("Double: " + i * 2);
}
59. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Unboxing
List<Integer> list =
Arrays.asList(Integer.valueOf(1), Integer.valueOf(2));
for (Integer i : list) {
System.out.println("Double: " + i.intValue() * 2);
}
60. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Varargs
List<Integer> list =
Arrays.asList(Integer.valueOf(1), Integer.valueOf(2));
for (Integer i : list) {
System.out.println("Double: " + i.intValue() * 2);
}
61. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Varargs
List<Integer> list =
Arrays.asList(Integer.valueOf(1), Integer.valueOf(2));
for (Integer i : list) {
System.out.println("Double: " + i.intValue() * 2);
}
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
62. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Varargs
List<Integer> list =
Arrays.asList(new Integer[]{Integer.valueOf(1), Integer.valueOf(2)});
for (Integer i : list) {
System.out.println("Double: " + i.intValue() * 2);
}
63. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• For each loop
List<Integer> list =
Arrays.asList(new Integer[]{Integer.valueOf(1), Integer.valueOf(2)});
for (Integer i : list) {
System.out.println("Double: " + i.intValue() * 2);
}
64. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• For each loop
public interface Iterable<T> {
Iterator<T> iterator();
}
List<Integer> list =
Arrays.asList(new Integer[]{Integer.valueOf(1), Integer.valueOf(2)});
for (Integer i : list) {
System.out.println("Double: " + i.intValue() * 2);
}
static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
65. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• For each loop
List<Integer> list =
Arrays.asList(new Integer[]{Integer.valueOf(1), Integer.valueOf(2)});
for (;;) {
Integer i
System.out.println("Double: " + i.intValue() * 2);
}
66. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• For each loop
List<Integer> list =
Arrays.asList(new Integer[]{Integer.valueOf(1), Integer.valueOf(2)});
for (Iterator i$ = list.iterator();;) {
Integer i
System.out.println("Double: " + i.intValue() * 2);
}
67. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• For each loop
List<Integer> list =
Arrays.asList(new Integer[]{Integer.valueOf(1), Integer.valueOf(2)});
for (Iterator i$ = list.iterator(); i$.hasNext();) {
Integer i
System.out.println("Double: " + i.intValue() * 2);
}
68. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• For each loop
List<Integer> list =
Arrays.asList(new Integer[]{Integer.valueOf(1), Integer.valueOf(2)});
for (Iterator i$ = list.iterator(); i$.hasNext();) {
Integer i = (Integer)i$.next();
System.out.println("Double: " + i.intValue() * 2);
}
69. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Enums
public enum Status {
YES, NO, MAYBE
}
70. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Enums
public enum Status {
YES, NO, MAYBE
private Status(String $enum$name, int $enum$ordinal) {
super($enum$name, $enum$ordinal);
}
}
71. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Enums
public enum Status {
YES, NO, MAYBE
private Status(String $enum$name, int $enum$ordinal) {
super($enum$name, $enum$ordinal);
}
public static Status valueOf(String name) {
return (Status)Enum.valueOf(Status.class, name);
}
}
72. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Enums
public enum Status {
YES, NO, MAYBE
private Status(String $enum$name, int $enum$ordinal) {
super($enum$name, $enum$ordinal);
}
public static Status valueOf(String name) {
return (Status)Enum.valueOf(Status.class, name);
}
private static final Status[] $VALUES = new Status[]{
Status.YES, Status.NO, Status.MAYBE};
public static Status[] values() {
return (Status[])$VALUES.clone();
}
73. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Enum based switch statements
public class SwitchStatus {
void switchStatus(Status status) {
switch (status) {
case MAYBE:
return;
default:
break;
}
}
}
74. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Enum based switch statements
public class SwitchStatus {
void switchStatus(Status status) {
switch (status) {
case MAYBE:
return;
default:
break;
}
}
}
75. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Enum based switch statements
public class SwitchStatus {
void switchStatus(Status status) {
switch (SwitchStatus$1.$SwitchMap$Status[(status).ordinal()]) {
case 1:
return;
default:
break;
}
}
}
76. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
class SwitchStatus$1 {
• Enum based switch statements
public class SwitchStatus {
void switchStatus(Status status) {
switch (SwitchStatus$1.$SwitchMap$Status[(status).ordinal()]) {
case 1:
return;
default:
break;
}
}
}
}
77. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
class SwitchStatus$1 {
static final int[] $SwitchMap$Status = new int[Status.values().length];
• Enum based switch statements
public class SwitchStatus {
void switchStatus(Status status) {
switch (SwitchStatus$1.$SwitchMap$Status[(status).ordinal()]) {
case 1:
return;
default:
break;
}
}
}
}
78. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
class SwitchStatus$1 {
static final int[] $SwitchMap$Status = new int[Status.values().length];
[0][0][0]
• Enum based switch statements
public class SwitchStatus {
void switchStatus(Status status) {
switch (SwitchStatus$1.$SwitchMap$Status[(status).ordinal()]) {
case 1:
return;
default:
break;
}
}
}
}
79. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
class SwitchStatus$1 {
static final int[] $SwitchMap$Status = new int[Status.values().length];
[0][0][1]
static {
• Enum based switch statements
try {
SwitchStatus$1.$SwitchMap$Status[Status.MAYBE.ordinal()] = 1;
} catch public (NoSuchFieldError class SwitchStatus ex) {
{ }
}
}
void switchStatus(Status status) {
switch (SwitchStatus$1.$SwitchMap$Status[(status).ordinal()]) {
case 1:
return;
default:
break;
}
}
}
80. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
class SwitchStatus$1 {
static final int[] $SwitchMap$Status = new int[Status.values().length];
[0][0][1]
static {
• Enum based switch statements
try {
SwitchStatus$1.$SwitchMap$Status[Status.MAYBE.ordinal()] = 1;
} catch public (NoSuchFieldError class SwitchStatus ex) {
{ }
}
}
void switchStatus(Status status) {
switch (SwitchStatus$1.$SwitchMap$Status[(status).ordinal()]) {
case 1:
return;
default:
break;
}
}
}
81. Parse Enter Process Attribute Flow Desugar Generate
processAnnotations(enterTrees(…
parseFiles(…))),…)
…
generate(desugar(flow(attribute(…))))
82. Parse Enter Process Attribute Flow Desugar Generate
• Generate bytecodes
• Organize initializers
• String concatenation
• Some optimizations
83. Parse Enter Process Attribute Flow Desugar Generate
• Organize <init> (Constructor)
public class InstanceInitialization {
String key="key";
String value;
public InstanceInitialization(String value) {
this.value = value;
}
}
84. Parse Enter Process Attribute Flow Desugar Generate
• Organize <init> (Constructor)
public class InstanceInitialization {
String key="key";
String value;
public InstanceInitialization(String value) {
super();
this.value = value;
}
}
85. Parse Enter Process Attribute Flow Desugar Generate
• Organize <init> (Constructor)
public class InstanceInitialization {
String key;
String value;
public InstanceInitialization(String value) {
super();
key = ”key”;
this.value = value;
}
}
86. Parse Enter Process Attribute Flow Desugar Generate
• Organize <init> (Constructor)
public class InstanceInitialization {
String key;
String value;
public <init>(String value) {
super();
key = ”key”;
this.value = value;
}
}
87. Parse Enter Process Attribute Flow Desugar Generate
• Organize <clinit> (Static initialization)
public class StaticInitialization {
static String key="key";
static String value;
static {
value = value;
}
}
88. Parse Enter Process Attribute Flow Desugar Generate
• Organize <clinit> (Static initialization)
public class StaticInitialization {
static String key;
static String value;
static {
key="key”;
value = value;
}
}
89. Parse Enter Process Attribute Flow Desugar Generate
• Organize <clinit> (Static initialization)
public class StaticInitialization {
static String key;
static String value;
static void <clinit>{
key="key”;
value = value;
}
}
90. Parse Enter Process Attribute Flow Desugar Generate
• String concatenation
Source code “Generated code”
”string” + value
91. Parse Enter Process Attribute Flow Desugar Generate
Source code “Generated code”
”string” + value new StringBuilder()
92. Parse Enter Process Attribute Flow Desugar Generate
Source code “Generated code”
”string” + value new StringBuilder()
.append(”string”)
93. Parse Enter Process Attribute Flow Desugar Generate
Source code “Generated code”
”string” + value new StringBuilder()
.append(”string”)
.append(value)
94. Parse Enter Process Attribute Flow Desugar Generate
Source code “Generated code”
”string” + value new StringBuilder()
.append(”string”)
.append(value)
.toString()
95. Goto Generation
• Lucky for us there is a byte code for goto
• goto <addr>
96. Parse Enter Process Attribute Flow Desugar Generate
Source code PC Byte code
…
if (<test>)
{
<ifblock>
}
else
{
<elseblock>
}
<codeafter>
… …
9 ifeq
22
… <elseblock>
… goto
29
<stackmap>
22 <ifblock>
<stackmap>
29 <codeafter>
Code generator (Code.java)
Java >= 1.6:
marked as not alive.
Stack map frames must be
embedded at target of jump
Goto instruction added to list
of pending jumps (Chain.java)
instruction
Pending jumps processed
97. Parse Enter Process Attribute Flow Desugar Generate
• Scenario 1: go back
Source code PC Byte code
…
label: <somecode>
…
goto label;
…
20
<stackmap>
<somecode>
… goto 20
Used by goto?
Must emit stackmap
Emit goto to label and turn
code generation on again
98. Parse Enter Process Attribute Flow Desugar Generate
• Scenario 2: go forward
Source code PC Byte code
Label used?
• emit stack frame
• patch pending gotos
…
goto label;
…
label: <somecode>
…
…
goto
<stackmap>
29 <somecode>
Label position not yet known?
• emit goto
• add to list of pending gotos
• turn generation on again
29
99. Parse Enter Process Attribute Flow Desugar Generate
Gen.java - Labelled
public void visitLabelled(JCLabeledStatement tree) {
// if the label is used from gotos, have to emit stack map
if (tree.handler.isUsed(tree))
code.emitStackMap();
…
}
100. Parse Enter Process Attribute Flow Desugar Generate
Gen.java - Goto
public void visitGoto(JCGoto tree) {
tree.handler.addBranch(new Chain(code.emitJump(goto_), null, code.state.dup()),
tree.target);
//normally goto marks code as not alive, turn generation on
code.entryPoint();
}
Target position known?
• Yes – patch immediately
• No – add to list of pending gotos
103. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Lambda implementation in Java 8
– Language change
– Compilation
– Runtime support
• Many interesting design considerations
104. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Simple example
public Comparator<String> lambdaExample() {
return (String a, String b) -> a.compareTo(b);
}
105. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• LambdaToMethod.java
public Comparator<String> lambdaExample() {
return (String a, String b) -> a.compareTo(b);
}
/*synthetic*/
private static int
lambda$lambdaExample$0(
,
) {
return ;
}
final String a
final String b
a.compareTo(b)
106. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
public interface Comparator {/*erased*/
• int compare(Runtime
Object o1, Object o2);
}
public Comparator<String> lambdaExample() {
return <invokedynamic>LambdaMetafactory.metafactory(
}
final class LambdaExample$$Lambda$1/1834188994
implements Comparator {
private LambdaExample$$Lambda$1/1834188994()
public int compare(Object,Object)
}
/*synthetic*/
private static int
lambda$lambdaExample$0(
final String a,
final String b) {
return a.compareTo(b);
}
Lookup(LambdaExample), /*caller*/
"compare",
()Comparator, /*MethodType*/
(Object,Object)int, /*MethodType*/
lambda$lambdaExample$0, /*MethodHandle*/
(String,String)int); /*MethodType*/
107. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Runtime class generation
LambdaMetaFactory
• metaFactory(…)
• altMetaFactory(…)
InnerClassLambdaMetafactory
ASM
Lambda Class
108. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Serialization
Lambda
Instance
Serialize
Serialized
Lambda
Lambda
Meta
Factory
Deserialize
109. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Example with instance and local capture
private int c = 3;
public Comparator<String> withInstanceAndLocalCapture() {
int a = 1, b = 2;
return (String d, String e) -> d.compareTo(e) + a + b - c;
}
110. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Example with instance and local capture
private int c = 3;
public Comparator<String> withInstanceAndLocalCapture() {
int a = 1, b = 2;
return (String d, String e) -> d.compareTo(e) + a + b - c;
}
/*synthetic*/
private int lambda$withInstanceAndLocalCapture$1(
final int cap$0, final int cap$1,
final String d, final String e) {
return d.compareTo(e) + cap$0 + cap$1 - c;
}
111. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Example with instance and local capture
private int c = 3;
public Comparator<String> withInstanceAndLocalCapture() {
int a = 1, b = 2;
return <invokedynamic>LambdaMetafactory.metafactory(this,a,b);
}
/*synthetic*/
private int lambda$withInstanceAndLocalCapture$1(
final int cap$0, final int cap$1,
final String d, final String e) {
return d.compareTo(e) + cap$0 + cap$1 - c;
}
112. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Example with instance and local capture
final class LambdaExample$$Lambda$2/2117255219 extends Object implements
Comparator {
public int compare(Object, Object)
private final LambdaExample arg$1;
private final int arg$2;
private final int arg$3;
private LambdaExample$$Lambda$2/2117255219(LambdaExample,int,int)
private static Comparator get$Lambda(LambdaExample,int,int)
}
.lambda$withInstanceAndLocalCapture$1(arg$arg$1 2, arg$3,(String)d,(String)e)
113. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Possible to back port ?
– Capture generated class ?
– Compile time generation of inner class!
114. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Possible to back port ?
– Capture generated class ?
– Compile time generation of inner class!
115. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Step 1: Enable lambda : Source.java
public boolean allowLambda() {
return compareTo( ) >= 0;
}
JDK1_85
116. Parse Enter Process Attribute Flow Desugar Generate
• Step 2: Enable special handling
boolean mustBackportLambda() {
return this.target.compareTo(Target.JDK1_8) < 0;
}
TransTypes Unlambda Lower
117. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Step 3: Implement and call lambda method
if(!this.attr.mustBackportLambda())
result = makeMetafactoryIndyCall(...);
else
{
result = new LambdaBackPorter(...).implementLambdaClass(...);
}
118. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Step 3: Example
private static final class Lambda$$2 implements Comparator<String> {
Lambda$$2 {
super();
}
public int compare(String arg0, String arg1) {
return LambdaExample.lambda$lambdaExample$0(arg0, arg1);
}
public int compare(Object o1, Object o2) {
return this.compare((String)o1, (String)o2);
}
}
119. Parse Enter Process Attribute Flow Desugar Generate
TransTypes Unlambda Lower
• Step 3: Example
public Comparator<String> lambdaExample() {
}
return LnaewmbLdaamMbedta$fa$c2t8o9r(y).;metafactory();
123. Thank You for
Your Time!
Martin Skarsaune
Java Developer and Co-Owner
if(you.want(nineBot))
goto Kantega;
Kantega:
eat(bbq);
test(java.skills);
Notas del editor
Hello everyone.
My name is Martin Skarsaune, developer working for Kantega.
Today we are going to have a peek into the compiler. I assume everyone here rely on a Java compiler in their everyday work.
Just out of curiosity, have anyone had a look at the compiler source code ?
I guess a lot of clever developers out here have read articles, have debugged a lot of Java applications and disassempled class files to see what your Java code turns into on the other end.
So some of the stuff may be known to you, never the less I think it is worth while to look at the process step by step from the inside and see with out own eyes what goes on.
So we want to implement GOTO statements.
So lets define what and how we want to achieve it.
We want to use the following syntax:
The keyword goto followed by an identifier and terminated by semicolon as other statements in Java.
The good news is that goto is already a reserved word in Java, so we do not have to worry about breaking old programs, as no one will have used the string goto in existing code.
As for the runtime behaviour, naturally a goto should make the program jump to the target statement.
We also require some more compiler rules.
First of all we require the target statement label to be defined in the same scope or block.
This is a sensible simplification for the purpose of this presentation, as the runtime implications would be very complex if you allowed jumps in and out of scopes.
If we do not find the label in the scope, we must give a compilation error.
Then we also want to do some analysis to detect potential circular gotos. In which case we should give a warning for the statement.
We choose warning instead of error here, as if you are inclined to use goto statements, you are probably quite crazy and not very happy about over protective compilers.
So we want to implement GOTO statements.
So lets define what and how we want to achieve it.
We want to use the following syntax:
The keyword goto followed by an identifier and terminated by semicolon as other statements in Java.
The good news is that goto is already a reserved word in Java, so we do not have to worry about breaking old programs, as no one will have used the string goto in existing code.
As for the runtime behaviour, naturally a goto should make the program jump to the target statement.
We also require some more compiler rules.
First of all we require the target statement label to be defined in the same scope or block.
This is a sensible simplification for the purpose of this presentation, as the runtime implications would be very complex if you allowed jumps in and out of scopes.
If we do not find the label in the scope, we must give a compilation error.
Then we also want to do some analysis to detect potential circular gotos. In which case we should give a warning for the statement.
We choose warning instead of error here, as if you are inclined to use goto statements, you are probably quite crazy and not very happy about over protective compilers.
First we have a success test case,
This code should compile without any errors or warnings.
When running the code goto statements should execute successfully.
To make things clear, we have labelled all statements in the example.
If we try to follow the execution
A compiler is a tool that accepts input in some sort of programming language and produces output targeted for another language or instruction set.
A common compiler architecture is to try to decouple the input from the output. The front end deals with specifics handling, checks and optimizations for the given programming language, and the back end deals with generation, dependencies and optimizations for a given target platform. This separation is an obvious choice for compilers that generate native code.
I was involved in a big project over several years, where we developed tools and routines for automated translation from Smalltalk to Java. In that project we did exactly this, we reused the parser and internal representation from the built in Smalltalk compiler, but instead of generating Smalltalk byte codes, we generated Java source code.
For the Java compiler there are a number of main stages that form a pipeline, where the output of one stage is input to the next, this is illustrated by the code snippet below. This first diagram shows the initial stages of a compilation. As you know a pipeline is a powerful abstraction where you may separate the work into logic chunks.
For the Java compiler there are a number of main stages that form a pipeline, where the output of one stage is input to the next, this is illustrated by the code snippet below. This first diagram shows the initial stages of a compilation. As you know a pipeline is a powerful abstraction where you may separate the work into logic chunks.
Did you know that goto is a reserved word in Java?
You may really wonder what they were thinking about?
But we will certainly make good use of it!
What other benefits are there to the fact that goto is a reserved word?
That means that until this day no one have been able to compile Java code with goto statements.
This way our changes to the language will not break any old code.
We will only break new code in spectacular ways.
So what is a sensible object structure for a compiler in transition between front and back end ?
Commonly an Abstract Syntax Tree (or AST) is used.
It is called abstract as it aims to be an abstraction away from specifics of both front and back end.
This simplified example tries to illustrate a typical structure and contents of an AST.
If you have for instance worked with Eclipse JDT plugins or ASM, you will recognize the similarities.
We see that terms used are quite generic, and not specifically tied to either input or output.
The sample items here are typically found in any modern programming language.
Even if you have no hands on experience with compilers, you will probably recognize the main components here from your own mental model and understanding of Java applications.
At the top (or left in this picture) we see a compilation unit, which in our case would be a Java source file.
Each compilation unit contains one or more class definitions, we do not distinguish between classes and interfaces on this level.
Each Class definition may in turn contain fields and method.
Within each method there are zero or more statements.
We also see that the syntax tree is strongly typed and attempts to give as precise, rich and detailed representation of the underlying source as possible. For instance an if statement would have dedicated expression for the condition, and separate blocks for the if and else part.
Typically the syntax tree is gradually refined and enriched in several stages to simplify type inference, analysis and generation.
As we will see later, processing of the syntax tree is often based on visitors. This has the advantage that you may insert logic directly where it is relevant, without having to tackle the entire structure. The visitor works its way down the tree with callbacks at each level, like this.
So if you want to make changes only relevant for method definitions, you insert your code in the respective method.
So what should we do to implement this in the compiler.
First of all, since we introduce a new syntactic element we have to create a new kind of syntax node.
There are parallell hierarchies for implementation and interface of different nodes.
So we create the interface GotoTree and the class JCGoto respectively.
The target of the goto is represented as a Name type.
So what should we do to implement this in the compiler.
First of all, since we introduce a new syntactic element we have to create a new kind of syntax node.
There are parallell hierarchies for implementation and interface of different nodes.
So we create the interface GotoTree and the class JCGoto respectively.
The target of the goto is represented as a Name type.
In addition, to simplify the presentation a bit, it is helpful with a helper object to keep track of labeled statements and goto statements in a given scope.
We will come back to why this is helpful.
Also for the purposes of presentation it is helpful to separate the goto specific code from where we need to interface with the existing code.
For the Java compiler there are a number of main stages, where the output of one stage is input to the next, this is illustrated by the code snippet below. This first diagram shows the initial stages of a compilation. Below each stage is the enum value used to represent the state of the compiler during the phase.
So we want to implement GOTO statements.
So lets define what and how we want to achieve it.
We want to use the following syntax:
The keyword goto followed by an identifier and terminated by semicolon as other statements in Java.
The good news is that goto is already a reserved word in Java, so we do not have to worry about breaking old programs, as no one will have used the string goto in existing code.
As for the runtime behaviour, naturally a goto should make the program jump to the target statement.
We also require some more compiler rules.
First of all we require the target statement label to be defined in the same scope or block.
This is a sensible simplification for the purpose of this presentation, as the runtime implications would be very complex if you allowed jumps in and out of scopes.
If we do not find the label in the scope, we must give a compilation error.
Then we also want to do some analysis to detect potential circular gotos. In which case we should give a warning for the statement.
We choose warning instead of error here, as if you are inclined to use goto statements, you are probably quite crazy and not very happy about over protective compilers.