-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDependencyHoister.java
More file actions
155 lines (136 loc) · 7.03 KB
/
Copy pathDependencyHoister.java
File metadata and controls
155 lines (136 loc) · 7.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package processing.mode.cpp;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Replaces the two iterative dependency-hoisting blocks inside
* CppBuild.java's writeSketch(): the free-function pass and the
* plain-variable pass that follows it. Both move top-level declarations
* out to namespace scope when something already hoisted (a class, or in
* the function pass's case also a previously-hoisted function) references
* them by name -- since a class hoisted to namespace scope cannot see a
* Sketch member.
*
* Ported from a parallel implementation's CppDependencyHoister (package
* processing.mode.cpp), retargeted onto this codebase's AST and
* NameUsageScanner port. See DECISION_two_parser_implementations.md.
*
* DEPARTURE from the port source: that implementation deduplicates
* "already hoisted this exact FunctionDecl" by rendering both candidates
* through its CppCodeGen and comparing the generated text. This codebase
* has not ported codegen (not yet needed for anything else), so dedup
* here instead compares node IDENTITY (reference equality) -- every
* FunctionDecl considered comes from the SAME immutable `remaining` list
* this pass is iterating and removing from, so a node is either literally
* the same object already in hoistedFunctions or it isn't; there is no
* scenario in this AST (unlike a hypothetical regenerated/rewritten one)
* where two distinct FunctionDecl objects represent the same source
* declaration and would need a textual-equality fallback. This is simpler
* than the original AND correct for this AST's actual usage pattern, not
* a weaker approximation of it.
*
* Preserves two original behaviors exactly, including ones that look like
* they might be oversights but are deliberately kept as-is rather than
* "improved," since changing them would change generated output for
* sketches that currently build correctly:
*
* 1. The variable pass's dependency haystack is "hoisted classes plus
* hoisted functions" -- it does NOT include previously-hoisted
* variables. The function pass's haystack DOES include its own
* accumulator. This asymmetry is preserved rather than "fixed."
* 2. A function reference must look like a CALL to count as a dependency
* for the function pass; a bare variable reference (no call) counts
* for the variable pass.
* 3. Lifecycle method/variable names (setup, draw, mousePressed, etc.)
* are never hoisted by either pass.
*/
final class DependencyHoister {
private DependencyHoister() {}
public static final class Result {
public final List<FunctionDecl> hoistedFunctions = new ArrayList<>();
public final List<VariableDecl> hoistedVariables = new ArrayList<>();
public final List<TopLevelItem> rest = new ArrayList<>();
}
/**
* @param items the remaining top-level items after class hoisting and
* array hoisting have already removed their own pieces
* @param hoistedClasses the already-hoisted TypeDef nodes (used as the
* initial dependency haystack for both sub-passes)
* @param lifecycleNames names that must never be hoisted by either pass
*/
public static Result hoist(List<TopLevelItem> items, List<TypeDef> hoistedClasses, Set<String> lifecycleNames) {
Result result = new Result();
List<TopLevelItem> remaining = new ArrayList<>(items);
// =================================================================
// PASS 1: free functions, called from hoisted classes or from
// previously-hoisted functions (its own accumulator IS included).
// =================================================================
boolean hoistedSomething = true;
while (hoistedSomething) {
hoistedSomething = false;
for (int i = 0; i < remaining.size(); i++) {
TopLevelItem item = remaining.get(i);
if (!(item instanceof FunctionDecl fd)) continue;
if (fd.isConstructor() || fd.isDestructor()) continue;
if (lifecycleNames.contains(fd.name())) continue;
if (result.hoistedFunctions.contains(fd)) continue; // identity-based, see class javadoc
boolean calledFromHoisted = isCalledByAnyTypeDef(fd.name(), hoistedClasses)
|| isCalledByAnyFunctionDecl(fd.name(), result.hoistedFunctions);
if (!calledFromHoisted) continue;
result.hoistedFunctions.add(fd);
remaining.remove(i);
hoistedSomething = true;
break; // restart scan, matching the original's "break after one hoist" restart behavior
}
}
// =================================================================
// PASS 2: plain variables, referenced (as a bare identifier, not
// necessarily a call) from hoisted classes or hoisted functions
// ONLY -- deliberately NOT from other already-hoisted variables.
// =================================================================
boolean hoistedVarSomething = true;
while (hoistedVarSomething) {
hoistedVarSomething = false;
for (int i = 0; i < remaining.size(); i++) {
TopLevelItem item = remaining.get(i);
if (!(item instanceof VariableDecl vd)) continue;
if (!vd.arrayDims().isEmpty()) continue; // arrays are ArrayHoister's job, not this pass's
if (lifecycleNames.contains(vd.name())) continue;
if (result.hoistedVariables.contains(vd)) continue;
boolean referencedFromHoisted = isBareIdentifierReferencedByAnyTypeDef(vd.name(), hoistedClasses)
|| isBareIdentifierReferencedByAnyFunctionDecl(vd.name(), result.hoistedFunctions);
if (!referencedFromHoisted) continue;
result.hoistedVariables.add(vd);
remaining.remove(i);
hoistedVarSomething = true;
break;
}
}
result.rest.addAll(remaining);
return result;
}
private static boolean isCalledByAnyTypeDef(String name, List<TypeDef> typeDefs) {
for (TypeDef td : typeDefs) {
if (NameUsageScanner.containsCall(td, name)) return true;
}
return false;
}
private static boolean isCalledByAnyFunctionDecl(String name, List<FunctionDecl> fns) {
for (FunctionDecl fd : fns) {
if (NameUsageScanner.containsCall(fd, name)) return true;
}
return false;
}
private static boolean isBareIdentifierReferencedByAnyTypeDef(String name, List<TypeDef> typeDefs) {
for (TypeDef td : typeDefs) {
if (NameUsageScanner.containsIdentifier(td, name)) return true;
}
return false;
}
private static boolean isBareIdentifierReferencedByAnyFunctionDecl(String name, List<FunctionDecl> fns) {
for (FunctionDecl fd : fns) {
if (NameUsageScanner.containsIdentifier(fd, name)) return true;
}
return false;
}
}