/
JavaParsingAtomicQueueGenerator.java
371 lines (322 loc) · 14.3 KB
/
JavaParsingAtomicQueueGenerator.java
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
package org.jctools.queues.atomic;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.Modifier.Keyword;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.PackageDeclaration;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.ast.expr.AssignExpr;
import com.github.javaparser.ast.expr.AssignExpr.Operator;
import com.github.javaparser.ast.expr.ClassExpr;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.SimpleName;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import com.github.javaparser.ast.expr.ThisExpr;
import com.github.javaparser.ast.nodeTypes.NodeWithType;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.stmt.ReturnStmt;
import com.github.javaparser.ast.type.ArrayType;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import org.jctools.queues.util.JCToolsGenerator;
/**
* Base class of the atomic queue generators. These generators work by parsing a Java source file using
* {@link JavaParser}, and replacing idioms that use {@link sun.misc.Unsafe} to instead use atomic field updates,
* e.g.{@link java.util.concurrent.atomic.AtomicLongFieldUpdater}. They are coupled directly to the structure of the
* expected input Java source file and are used as a utility to maintain unsafe non-portable optimized code along side
* safe portable code for uses such as on Android, etc
* <p>
* These generators are coupled with the structure and naming of fields, variables and methods and are not suitable for
* general purpose use.
*/
public abstract class JavaParsingAtomicQueueGenerator extends VoidVisitorAdapter<Void> implements JCToolsGenerator {
/**
* When set on a class using a single line comment, the class has fields that have unsafe 'ordered' reads and
* writes. These fields are candidates to be patched by the generator. Other classes the fields remain unadjusted.
*/
protected static final String GEN_DIRECTIVE_CLASS_CONTAINS_ORDERED_FIELD_ACCESSORS = "$gen:ordered-fields";
/**
* When set on a method using a single line comment, the method is not patched by the generator.
*/
protected static final String GEN_DIRECTIVE_METHOD_IGNORE = "$gen:ignore";
protected final String sourceFileName;
protected String outputPackage() {
return "org.jctools.queues.atomic";
}
protected String queueClassNamePrefix() {
return "Atomic";
}
JavaParsingAtomicQueueGenerator(String sourceFileName) {
this.sourceFileName = sourceFileName;
}
abstract void processSpecialNodeTypes(NodeWithType<?, Type> node, String name);
abstract String fieldUpdaterFieldName(String fieldName);
@Override
public void visit(PackageDeclaration n, Void arg) {
super.visit(n, arg);
// Change the package of the output
n.setName(outputPackage());
}
@Override
public void visit(Parameter n, Void arg) {
super.visit(n, arg);
// Process parameters to methods and ctors
processSpecialNodeTypes(n);
}
@Override
public void visit(VariableDeclarator n, Void arg) {
super.visit(n, arg);
// Replace declared variables with altered types
processSpecialNodeTypes(n);
}
private void processSpecialNodeTypes(Parameter node) {
processSpecialNodeTypes(node, node.getNameAsString());
}
private void processSpecialNodeTypes(VariableDeclarator node) {
processSpecialNodeTypes(node, node.getNameAsString());
}
protected boolean isCommentPresent(Node node, String wanted) {
Optional<Comment> maybeComment = node.getComment();
if (maybeComment.isPresent()) {
Comment comment = maybeComment.get();
String content = comment.getContent().trim();
if (wanted.equals(content)) {
return true;
}
}
return false;
}
protected void removeStaticFieldsAndInitialisers(ClassOrInterfaceDeclaration node) {
// Remove all the static initialisers
for (InitializerDeclaration child : node.getChildNodesByType(InitializerDeclaration.class)) {
child.remove();
}
// Remove all static fields
for (FieldDeclaration field : node.getFields()) {
if (field.getModifiers().contains(Modifier.staticModifier())) {
field.remove();
}
}
}
@Override
public String translateQueueName(String qName) {
if (qName.contains("LinkedQueue") || qName.contains("LinkedArrayQueue")) {
return qName.replace("Linked", "Linked" + queueClassNamePrefix());
}
if (qName.contains("ArrayQueue")) {
return qName.replace("ArrayQueue", queueClassNamePrefix() + "ArrayQueue");
}
throw new IllegalArgumentException("Unexpected queue name: " + qName);
}
boolean patchAtomicFieldUpdaterAccessorMethod(String variableName, MethodDeclaration method, String methodNameSuffix)
{
boolean usesFieldUpdater = false;
String methodName = method.getNameAsString();
if (!methodName.endsWith(methodNameSuffix)) {
// Leave it untouched
return false;
}
String newValueName = "newValue";
if (methodName.startsWith("so") || methodName.startsWith("sp"))
{
/*
* In the case of 'sp' use lazySet as the weakest
* ordering allowed by field updaters
*/
usesFieldUpdater = true;
String fieldUpdaterFieldName = fieldUpdaterFieldName(variableName);
method.setBody(fieldUpdaterLazySet(fieldUpdaterFieldName, newValueName));
}
else if (methodName.startsWith("cas"))
{
usesFieldUpdater = true;
String fieldUpdaterFieldName = fieldUpdaterFieldName(variableName);
String expectedValueName = "expect";
method.setBody(
fieldUpdaterCompareAndSet(fieldUpdaterFieldName, expectedValueName, newValueName));
}
else if (methodName.startsWith("sv"))
{
method.setBody(fieldAssignment(variableName, newValueName));
}
else if (methodName.startsWith("lv") || methodName.startsWith("lp"))
{
method.setBody(returnField(variableName));
}
else
{
throw new IllegalStateException("Unhandled method: " + methodName);
}
return usesFieldUpdater;
}
/**
* Searches all extended or implemented super classes or interfaces for
* special classes that differ with the atomics version and replaces them
* with the appropriate class.
*/
protected void replaceParentClassesForAtomics(ClassOrInterfaceDeclaration n) {
for (ClassOrInterfaceType parent : n.getExtendedTypes()) {
String parentNameAsString = parent.getNameAsString();
switch (parentNameAsString) {
case "AbstractQueue":
// ignore the JDK parent
break;
case "BaseLinkedQueue":
parent.setName("BaseLinked" + queueClassNamePrefix() + "Queue");
break;
case "ConcurrentCircularArrayQueue":
parent.setName("AtomicReferenceArrayQueue");
break;
case "ConcurrentSequencedCircularArrayQueue":
parent.setName("SequencedAtomicReferenceArrayQueue");
break;
default:
// Padded super classes are to be renamed and thus so does the
// class we must extend.
parent.setName(translateQueueName(parentNameAsString));
}
}
}
@Override
public void cleanupComments(CompilationUnit cu) {
// nop
}
@Override
public void organiseImports(CompilationUnit cu) {
List<ImportDeclaration> importDecls = new ArrayList<>();
// remove irrelevant imports
for (ImportDeclaration importDeclaration : cu.getImports()) {
String name = importDeclaration.getNameAsString();
if (name.startsWith("org.jctools.util.Unsafe")) {
continue;
}
if (name.startsWith("org.jctools.queues.LinkedArrayQueueUtil")) {
continue;
}
importDecls.add(importDeclaration);
}
cu.getImports().clear();
for (ImportDeclaration importDecl : importDecls) {
cu.addImport(importDecl);
}
cu.addImport(new ImportDeclaration("java.util.concurrent.atomic", false, true));
cu.addImport(new ImportDeclaration("org.jctools.queues", false, true));
cu.addImport(staticImportDeclaration("org.jctools.queues.atomic.AtomicQueueUtil"));
}
protected String capitalise(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
/**
* Generates something like
* <code>P_INDEX_UPDATER.lazySet(this, newValue)</code>
*/
protected BlockStmt fieldUpdaterLazySet(String fieldUpdaterFieldName, String newValueName) {
BlockStmt body = new BlockStmt();
body.addStatement(new ExpressionStmt(
methodCallExpr(fieldUpdaterFieldName, "lazySet", new ThisExpr(), new NameExpr(newValueName))));
return body;
}
/**
* Generates something like
* <code>return P_INDEX_UPDATER.compareAndSet(this, expectedValue, newValue)</code>
*/
protected BlockStmt fieldUpdaterCompareAndSet(String fieldUpdaterFieldName, String expectedValueName,
String newValueName) {
BlockStmt body = new BlockStmt();
body.addStatement(new ReturnStmt(methodCallExpr(fieldUpdaterFieldName, "compareAndSet", new ThisExpr(),
new NameExpr(expectedValueName), new NameExpr(newValueName))));
return body;
}
protected MethodCallExpr methodCallExpr(String owner, String method, Expression... args) {
MethodCallExpr methodCallExpr = new MethodCallExpr(new NameExpr(owner), method);
for (Expression expr : args) {
methodCallExpr.addArgument(expr);
}
return methodCallExpr;
}
/**
* Generates something like <code>field = newValue</code>
*/
protected BlockStmt fieldAssignment(String fieldName, String valueName) {
BlockStmt body = new BlockStmt();
body.addStatement(
new ExpressionStmt(new AssignExpr(new NameExpr(fieldName), new NameExpr(valueName), Operator.ASSIGN)));
return body;
}
/**
* Generates something like
* <code>private static final AtomicLongFieldUpdater<MpmcAtomicArrayQueueProducerIndexField> P_INDEX_UPDATER = AtomicLongFieldUpdater.newUpdater(MpmcAtomicArrayQueueProducerIndexField.class, "producerIndex");</code>
*/
protected FieldDeclaration fieldDeclarationWithInitialiser(Type type, String name, Expression initializer,
Keyword... modifiers) {
FieldDeclaration fieldDeclaration = new FieldDeclaration();
VariableDeclarator variable = new VariableDeclarator(type, name, initializer);
fieldDeclaration.getVariables().add(variable);
fieldDeclaration.setModifiers(modifiers);
return fieldDeclaration;
}
/**
* Generates something like
* <code>private static final AtomicLongFieldUpdater<MpmcAtomicArrayQueueProducerIndexField> P_INDEX_UPDATER = AtomicLongFieldUpdater.newUpdater(MpmcAtomicArrayQueueProducerIndexField.class, "producerIndex");</code>
*/
protected FieldDeclaration declareLongFieldUpdater(String className, String variableName) {
MethodCallExpr initializer = newAtomicLongFieldUpdater(className, variableName);
ClassOrInterfaceType type = simpleParametricType("AtomicLongFieldUpdater", className);
return fieldDeclarationWithInitialiser(type, fieldUpdaterFieldName(variableName),
initializer, Keyword.PRIVATE, Keyword.STATIC, Keyword.FINAL);
}
protected MethodCallExpr newAtomicLongFieldUpdater(String className, String variableName) {
return methodCallExpr("AtomicLongFieldUpdater", "newUpdater", new ClassExpr(classType(className)),
new StringLiteralExpr(variableName));
}
protected ClassOrInterfaceType simpleParametricType(String className, String... typeArgs) {
NodeList<Type> typeArguments = new NodeList<Type>();
for (String typeArg : typeArgs) {
typeArguments.add(classType(typeArg));
}
return new ClassOrInterfaceType(null, new SimpleName(className), typeArguments);
}
protected ClassOrInterfaceType classType(String className) {
return new ClassOrInterfaceType(null, className);
}
/**
* Generates something like <code>return field</code>
*
*/
protected BlockStmt returnField(String fieldName) {
BlockStmt body = new BlockStmt();
body.addStatement(new ReturnStmt(fieldName));
return body;
}
protected boolean isRefArray(Type in, String refClassName) {
if (in instanceof ArrayType) {
ArrayType aType = (ArrayType) in;
return isRefType(aType.getComponentType(), refClassName);
}
return false;
}
protected boolean isRefType(Type in, String className) {
// Does not check type parameters
if (in instanceof ClassOrInterfaceType) {
return (className.equals(((ClassOrInterfaceType) in).getNameAsString()));
}
return false;
}
private static <T> T buildGenerator(Class<? extends T> generatorClass, String fileName) throws Exception {
return generatorClass.getDeclaredConstructor(String.class).newInstance(fileName);
}
ImportDeclaration staticImportDeclaration(String name) {
return new ImportDeclaration(name, true, true);
}
}