From 6d4cd1e811f22b3a05fdac206cc54cf8616042e8 Mon Sep 17 00:00:00 2001 From: Jan Nielsen Date: Wed, 6 Mar 2019 11:12:01 -0700 Subject: [PATCH] Polish -- DRY refactor of the two generators via a generator base class. --- .../JavaParsingAtomicArrayQueueGenerator.java | 417 +++------------ ...JavaParsingAtomicLinkedQueueGenerator.java | 486 ++++-------------- .../JavaParsingAtomicQueueGenerator.java | 309 +++++++++++ 3 files changed, 493 insertions(+), 719 deletions(-) create mode 100644 jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicQueueGenerator.java diff --git a/jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicArrayQueueGenerator.java b/jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicArrayQueueGenerator.java index 532375f3..bd6037a9 100644 --- a/jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicArrayQueueGenerator.java +++ b/jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicArrayQueueGenerator.java @@ -1,69 +1,46 @@ package org.jctools.queues.atomic; -import java.io.File; -import java.io.FileWriter; import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumSet; 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.Node; -import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.PackageDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.FieldDeclaration; -import com.github.javaparser.ast.body.InitializerDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.VariableDeclarator; -import com.github.javaparser.ast.comments.Comment; import com.github.javaparser.ast.comments.JavadocComment; -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.FieldAccessExpr; import com.github.javaparser.ast.expr.MarkerAnnotationExpr; import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.expr.Name; 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.PrimitiveType; import com.github.javaparser.ast.type.Type; -import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -public final class JavaParsingAtomicArrayQueueGenerator extends VoidVisitorAdapter { +public final class JavaParsingAtomicArrayQueueGenerator extends JavaParsingAtomicQueueGenerator { private static final String GEN_DIRECTIVE_CLASS_CONTAINS_ORDERED_FIELD_ACCESSORS = "$gen:ordered-fields"; private static final String GEN_DIRECTIVE_METHOD_IGNORE = "$gen:ignore"; - private static final String INDENT_LEVEL = " "; - private final String sourceFileName; - public JavaParsingAtomicArrayQueueGenerator(String sourceFileName) { - super(); - this.sourceFileName = sourceFileName; + public static void main(String[] args) throws Exception { + main(JavaParsingAtomicArrayQueueGenerator.class, args); + } + + JavaParsingAtomicArrayQueueGenerator(String sourceFileName) { + super(sourceFileName); } @Override - public void visit(FieldAccessExpr n, Void arg) { + public void visit(ConstructorDeclaration n, Void arg) { super.visit(n, arg); - if (n.getScope() instanceof NameExpr) { - NameExpr name = (NameExpr) n.getScope(); - name.setName(translateQueueName(name.getNameAsString())); - } + // Update the ctor to match the class name + n.setName(translateQueueName(n.getNameAsString())); } @Override @@ -88,7 +65,7 @@ public void visit(ClassOrInterfaceDeclaration node, Void arg) { if (!node.getMethodsByName("failFastOffer").isEmpty()) { MethodDeclaration deprecatedMethodRedirect = node.addMethod("weakOffer", Modifier.PUBLIC); - patchMethodAsDepreciatedRedirector(deprecatedMethodRedirect, "failFastOffer", PrimitiveType.intType(), + patchMethodAsDeprecatedRedirector(deprecatedMethodRedirect, "failFastOffer", PrimitiveType.intType(), new Parameter(classType("E"), "e")); } @@ -99,57 +76,73 @@ public void visit(ClassOrInterfaceDeclaration node, Void arg) { + node.getJavadocComment().orElse(new JavadocComment("")).getContent()); } - @Override - public void visit(ConstructorDeclaration n, Void arg) { - super.visit(n, arg); - // Update the ctor to match the class name - n.setName(translateQueueName(n.getNameAsString())); - } + String translateQueueName(String originalQueueName) { + if (originalQueueName.length() < 5) { + return originalQueueName; + } - @Override - public void visit(PackageDeclaration n, Void arg) { - super.visit(n, arg); - // Change the package of the output - n.setName("org.jctools.queues.atomic"); - } + String start = originalQueueName.substring(0, 4); + String end = originalQueueName.substring(4); + if ((start.equals("Spsc") || start.equals("Spmc") || start.equals("Mpsc") || start.equals("Mpmc")) + && end.startsWith("ArrayQueue")) { + return start + "Atomic" + end; + } - @Override - public void visit(Parameter n, Void arg) { - super.visit(n, arg); - // Process parameters to methods and ctors - processSpecialNodeTypes(n); + return originalQueueName; } - @Override - public void visit(VariableDeclarator n, Void arg) { - super.visit(n, arg); - // Replace declared variables with altered types - processSpecialNodeTypes(n); + String fieldUpdaterFieldName(String fieldName) { + switch (fieldName) { + case "producerIndex": + return "P_INDEX_UPDATER"; + case "consumerIndex": + return "C_INDEX_UPDATER"; + case "producerLimit": + return "P_LIMIT_UPDATER"; + default: + throw new IllegalArgumentException("Unhandled field: " + fieldName); + } } - private static boolean isCommentPresent(Node node, String wanted) { - Optional maybeComment = node.getComment(); - if (maybeComment.isPresent()) { - Comment comment = maybeComment.get(); - String content = comment.getContent().trim(); - if (wanted.equals(content)) { - return true; + void organiseImports(CompilationUnit cu) { + List importDecls = new ArrayList<>(); + for (ImportDeclaration importDeclaration : cu.getImports()) { + if (importDeclaration.getNameAsString().startsWith("org.jctools.util.Unsafe")) { + continue; } + importDecls.add(importDeclaration); } - return false; - } - - private static void removeStaticFieldsAndInitialisers(ClassOrInterfaceDeclaration node) { - // Remove all the static initialisers - for (InitializerDeclaration child : node.getChildNodesByType(InitializerDeclaration.class)) { - child.remove(); + cu.getImports().clear(); + for (ImportDeclaration importDecl : importDecls) { + cu.addImport(importDecl); } + cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicLongFieldUpdater")); + cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicReferenceArray")); + cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicLongArray")); + } - // Remove all static fields - for (FieldDeclaration field : node.getFields()) { - if (field.getModifiers().contains(Modifier.STATIC)) { - field.remove(); - continue; + /** + * Given a variable declaration of some sort, check it's name and type and + * if it looks like any of the key type changes between unsafe and atomic + * queues, perform the conversion to change it's type. + * + * @param node + * @param name + */ + void processSpecialNodeTypes(NodeWithType node, String name) { + Type type = node.getType(); + if ("buffer".equals(name) && isRefArray(type, "E")) { + node.setType(atomicRefArrayType((ArrayType) type)); + } else if ("sBuffer".equals(name) && isLongArray(type)) { + node.setType(atomicLongArrayType()); + } else if (PrimitiveType.longType().equals(type)) { + switch(name) { + case "mask": + case "offset": + case "seqOffset": + case "lookAheadSeqOffset": + case "lookAheadElementOffset": + node.setType(PrimitiveType.intType()); } } } @@ -161,7 +154,7 @@ private static void removeStaticFieldsAndInitialisers(ClassOrInterfaceDeclaratio * * @param n */ - private static void replaceParentClassesForAtomics(ClassOrInterfaceDeclaration n) { + private void replaceParentClassesForAtomics(ClassOrInterfaceDeclaration n) { for (ClassOrInterfaceType parent : n.getExtendedTypes()) { if ("ConcurrentCircularArrayQueue".equals(parent.getNameAsString())) { parent.setName("AtomicReferenceArrayQueue"); @@ -186,7 +179,7 @@ private static void replaceParentClassesForAtomics(ClassOrInterfaceDeclaration n * @param returnType * @param parameters */ - private static void patchMethodAsDepreciatedRedirector(MethodDeclaration methodToPatch, String toMethodName, + private void patchMethodAsDeprecatedRedirector(MethodDeclaration methodToPatch, String toMethodName, Type returnType, Parameter... parameters) { methodToPatch.setType(returnType); for (Parameter parameter : parameters) { @@ -217,7 +210,7 @@ private static void patchMethodAsDepreciatedRedirector(MethodDeclaration methodT * @param n * the AST node for the containing class */ - private static void patchAtomicFieldUpdaterAccessorMethods(ClassOrInterfaceDeclaration n) { + private void patchAtomicFieldUpdaterAccessorMethods(ClassOrInterfaceDeclaration n) { String className = n.getNameAsString(); for (FieldDeclaration field : n.getFields()) { @@ -271,141 +264,7 @@ private static void patchAtomicFieldUpdaterAccessorMethods(ClassOrInterfaceDecla } } - private static String capitalise(String s) { - return s.substring(0, 1).toUpperCase() + s.substring(1); - } - - private static String formatMultilineJavadoc(int indent, String... lines) { - String indentation = ""; - for (int i = 0; i < indent; i++) { - indentation += INDENT_LEVEL; - } - - String out = "\n"; - for (String line : lines) { - out += indentation + " * " + line + "\n"; - } - out += indentation + " "; - return out; - } - - /** - * Generates something like - * P_INDEX_UPDATER.lazySet(this, newValue) - * - * @param fieldUpdaterFieldName - * @param newValueName - * @return - */ - private static 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 - * return P_INDEX_UPDATER.compareAndSet(this, expectedValue, newValue) - * - * @param fieldUpdaterFieldName - * @param expectedValueName - * @param newValueName - * @return - */ - private static 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; - } - - /** - * Generates something like field = newValue - * - * @param fieldName - * @param valueName - * @return - */ - private static 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 - * private static final AtomicLongFieldUpdater P_INDEX_UPDATER = AtomicLongFieldUpdater.newUpdater(MpmcAtomicArrayQueueProducerIndexField.class, "producerIndex"); - * - * @param type - * @param name - * @param initializer - * @param modifiers - * @return - */ - private static FieldDeclaration fieldDeclarationWithInitialiser(Type type, String name, Expression initializer, - Modifier... modifiers) { - FieldDeclaration fieldDeclaration = new FieldDeclaration(); - VariableDeclarator variable = new VariableDeclarator(type, name, initializer); - fieldDeclaration.getVariables().add(variable); - EnumSet modifierSet = EnumSet.copyOf(Arrays.asList(modifiers)); - fieldDeclaration.setModifiers(modifierSet); - return fieldDeclaration; - } - - /** - * Generates something like - * private static final AtomicLongFieldUpdater P_INDEX_UPDATER = AtomicLongFieldUpdater.newUpdater(MpmcAtomicArrayQueueProducerIndexField.class, "producerIndex"); - * - * @param className - * @param variableName - * @return - */ - private static FieldDeclaration declareLongFieldUpdater(String className, String variableName) { - MethodCallExpr initializer = newAtomicLongFieldUpdater(className, variableName); - - ClassOrInterfaceType type = simpleParametricType("AtomicLongFieldUpdater", className); - FieldDeclaration newField = fieldDeclarationWithInitialiser(type, fieldUpdaterFieldName(variableName), - initializer, Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL); - return newField; - } - - private static MethodCallExpr newAtomicLongFieldUpdater(String className, String variableName) { - return methodCallExpr("AtomicLongFieldUpdater", "newUpdater", new ClassExpr(classType(className)), - new StringLiteralExpr(variableName)); - } - - /** - * Generates something like return field - * - * @param fieldName - * @return - */ - private static BlockStmt returnField(String fieldName) { - BlockStmt body = new BlockStmt(); - body.addStatement(new ReturnStmt(fieldName)); - return body; - } - - private static boolean isRefArray(Type in, String refClassName) { - if (in instanceof ArrayType) { - ArrayType aType = (ArrayType) in; - return isRefType(aType.getComponentType(), refClassName); - } - return false; - } - - private static 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 boolean isLongArray(Type in) { + private boolean isLongArray(Type in) { if (in instanceof ArrayType) { ArrayType aType = (ArrayType) in; return PrimitiveType.longType().equals(aType.getComponentType()); @@ -413,142 +272,14 @@ private static boolean isLongArray(Type in) { return false; } - private static ClassOrInterfaceType atomicRefArrayType(ArrayType in) { + private ClassOrInterfaceType atomicRefArrayType(ArrayType in) { ClassOrInterfaceType out = new ClassOrInterfaceType(null, "AtomicReferenceArray"); out.setTypeArguments(in.getComponentType()); return out; } - private static ClassOrInterfaceType atomicLongArrayType() { + private ClassOrInterfaceType atomicLongArrayType() { return new ClassOrInterfaceType(null, "AtomicLongArray"); } - private static 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; - } - - private static ClassOrInterfaceType simpleParametricType(String className, String... typeArgs) { - NodeList typeArguments = new NodeList(); - for (String typeArg : typeArgs) { - typeArguments.add(classType(typeArg)); - } - return new ClassOrInterfaceType(null, new SimpleName(className), typeArguments); - } - - private static ClassOrInterfaceType classType(String className) { - return new ClassOrInterfaceType(null, className); - } - - private static ImportDeclaration importDeclaration(String name) { - return new ImportDeclaration(new Name(name), false, false); - } - - private static String translateQueueName(String originalQueueName) { - if (originalQueueName.length() < 5) { - return originalQueueName; - } - - String start = originalQueueName.substring(0, 4); - String end = originalQueueName.substring(4); - if ((start.equals("Spsc") || start.equals("Spmc") || start.equals("Mpsc") || start.equals("Mpmc")) - && end.startsWith("ArrayQueue")) { - return start + "Atomic" + end; - } - - return originalQueueName; - } - - private static String fieldUpdaterFieldName(String fieldName) { - switch (fieldName) { - case "producerIndex": - return "P_INDEX_UPDATER"; - case "consumerIndex": - return "C_INDEX_UPDATER"; - case "producerLimit": - return "P_LIMIT_UPDATER"; - default: - throw new IllegalArgumentException("Unhandled field: " + fieldName); - } - } - - private static void organiseImports(CompilationUnit cu) { - List importDecls = new ArrayList<>(); - for (ImportDeclaration importDeclaration : cu.getImports()) { - if (importDeclaration.getNameAsString().startsWith("org.jctools.util.Unsafe")) { - continue; - } - importDecls.add(importDeclaration); - } - cu.getImports().clear(); - for (ImportDeclaration importDecl : importDecls) { - cu.addImport(importDecl); - } - cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicLongFieldUpdater")); - cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicReferenceArray")); - cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicLongArray")); - } - - private static void processSpecialNodeTypes(Parameter node) { - processSpecialNodeTypes(node, node.getNameAsString()); - } - - private static void processSpecialNodeTypes(VariableDeclarator node) { - processSpecialNodeTypes(node, node.getNameAsString()); - } - - /** - * Given a variable declaration of some sort, check it's name and type and - * if it looks like any of the key type changes between unsafe and atomic - * queues, perform the conversion to change it's type. - * - * @param node - * @param name - */ - private static void processSpecialNodeTypes(NodeWithType node, String name) { - Type type = node.getType(); - if ("buffer".equals(name) && isRefArray(type, "E")) { - node.setType(atomicRefArrayType((ArrayType) type)); - } else if ("sBuffer".equals(name) && isLongArray(type)) { - node.setType(atomicLongArrayType()); - } else if (PrimitiveType.longType().equals(type)) { - switch(name) { - case "mask": - case "offset": - case "seqOffset": - case "lookAheadSeqOffset": - case "lookAheadElementOffset": - node.setType(PrimitiveType.intType()); - } - } - } - - public static void main(String[] args) throws Exception { - - if (args.length < 2) { - throw new IllegalArgumentException("Usage: outputDirectory inputSourceFiles"); - } - - File outputDirectory = new File(args[0]); - - for (int i = 1; i < args.length; i++) { - File file = new File(args[i]); - System.out.println("Processing " + file); - CompilationUnit cu = JavaParser.parse(file); - new JavaParsingAtomicArrayQueueGenerator(file.getName()).visit(cu, null); - - organiseImports(cu); - - String outputFileName = translateQueueName(file.getName().replace(".java", "")) + ".java"; - - try (FileWriter writer = new FileWriter(new File(outputDirectory, outputFileName))) { - writer.write(cu.toString()); - } - System.out.println("Saved to " + outputFileName); - } - } - } diff --git a/jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicLinkedQueueGenerator.java b/jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicLinkedQueueGenerator.java index fa7b8a88..34722c61 100644 --- a/jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicLinkedQueueGenerator.java +++ b/jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicLinkedQueueGenerator.java @@ -1,79 +1,56 @@ package org.jctools.queues.atomic; -import java.io.File; -import java.io.FileWriter; import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumSet; 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.Node; import com.github.javaparser.ast.NodeList; -import com.github.javaparser.ast.PackageDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.FieldDeclaration; -import com.github.javaparser.ast.body.InitializerDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.VariableDeclarator; -import com.github.javaparser.ast.comments.Comment; import com.github.javaparser.ast.comments.JavadocComment; -import com.github.javaparser.ast.expr.AssignExpr; -import com.github.javaparser.ast.expr.AssignExpr.Operator; import com.github.javaparser.ast.expr.CastExpr; import com.github.javaparser.ast.expr.ClassExpr; -import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.ast.expr.FieldAccessExpr; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.Name; import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; -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.PrimitiveType; import com.github.javaparser.ast.type.Type; -import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -public final class JavaParsingAtomicLinkedQueueGenerator extends VoidVisitorAdapter { +public final class JavaParsingAtomicLinkedQueueGenerator extends JavaParsingAtomicQueueGenerator { private static final String GEN_DIRECTIVE_CLASS_CONTAINS_ORDERED_FIELD_ACCESSORS = "$gen:ordered-fields"; private static final String GEN_DIRECTIVE_METHOD_IGNORE = "$gen:ignore"; private static final String MPSC_LINKED_ATOMIC_QUEUE_NAME = "MpscLinkedAtomicQueue"; - private static final String INDENT_LEVEL = " "; - private final String sourceFileName; - public JavaParsingAtomicLinkedQueueGenerator(String sourceFileName) { - super(); - this.sourceFileName = sourceFileName; + public static void main(String[] args) throws Exception { + main(JavaParsingAtomicLinkedQueueGenerator.class, args); } - @Override - public void visit(FieldAccessExpr n, Void arg) { - super.visit(n, arg); - if (n.getScope() instanceof NameExpr) { - NameExpr name = (NameExpr) n.getScope(); - name.setName(translateQueueName(name.getNameAsString())); - } + JavaParsingAtomicLinkedQueueGenerator(String sourceFileName) { + super(sourceFileName); } - + @Override - public void visit(CastExpr n, Void arg) { + public void visit(ConstructorDeclaration n, Void arg) { super.visit(n, arg); - - if (isRefArray(n.getType(), "E")) { - n.setType(atomicRefArrayType((ArrayType) n.getType())); + // Update the ctor to match the class name + n.setName(translateQueueName(n.getNameAsString())); + if (MPSC_LINKED_ATOMIC_QUEUE_NAME.equals(n.getNameAsString())) { + // Special case for MPSC because the Unsafe variant has a static factory method and a protected constructor. + n.setModifier(Modifier.PROTECTED, false); + n.setModifier(Modifier.PUBLIC, true); } } @@ -111,38 +88,14 @@ public void visit(ClassOrInterfaceDeclaration node, Void arg) { } @Override - public void visit(ConstructorDeclaration n, Void arg) { + public void visit(CastExpr n, Void arg) { super.visit(n, arg); - // Update the ctor to match the class name - n.setName(translateQueueName(n.getNameAsString())); - if (MPSC_LINKED_ATOMIC_QUEUE_NAME.equals(n.getNameAsString())) { - // Special case for MPSC because the Unsafe variant has a static factory method and a protected constructor. - n.setModifier(Modifier.PROTECTED, false); - n.setModifier(Modifier.PUBLIC, true); + + if (isRefArray(n.getType(), "E")) { + n.setType(atomicRefArrayType((ArrayType) n.getType())); } } - @Override - public void visit(PackageDeclaration n, Void arg) { - super.visit(n, arg); - // Change the package of the output - n.setName("org.jctools.queues.atomic"); - } - - @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); - } - @Override public void visit(MethodDeclaration n, Void arg) { super.visit(n, arg); @@ -156,30 +109,96 @@ public void visit(ObjectCreationExpr n, Void arg) { processSpecialNodeTypes(n); } - private static boolean isCommentPresent(Node node, String wanted) { - Optional maybeComment = node.getComment(); - if (maybeComment.isPresent()) { - Comment comment = maybeComment.get(); - String content = comment.getContent().trim(); - if (wanted.equals(content)) { - return true; - } + String translateQueueName(String originalQueueName) { + if (originalQueueName.length() < 5) { + return originalQueueName; + } + + if (originalQueueName.contains("LinkedQueue") || originalQueueName.contains("LinkedArrayQueue")) { + return originalQueueName.replace("Linked", "LinkedAtomic"); + } + + if (originalQueueName.contains("ArrayQueue")) { + return originalQueueName.replace("ArrayQueue", "AtomicArrayQueue"); } - return false; + + return originalQueueName; } - private static void removeStaticFieldsAndInitialisers(ClassOrInterfaceDeclaration node) { - // Remove all the static initialisers - for (InitializerDeclaration child : node.getChildNodesByType(InitializerDeclaration.class)) { - child.remove(); + String fieldUpdaterFieldName(String fieldName) { + switch (fieldName) { + case "producerNode": + return "P_NODE_UPDATER"; + case "consumerNode": + return "C_NODE_UPDATER"; + case "consumerIndex": + return "C_INDEX_UPDATER"; + case "producerIndex": + return "P_INDEX_UPDATER"; + case "producerLimit": + return "P_LIMIT_UPDATER"; + default: + throw new IllegalArgumentException("Unhandled field: " + fieldName); } + } - // Remove all static fields - for (FieldDeclaration field : node.getFields()) { - if (field.getModifiers().contains(Modifier.STATIC)) { - field.remove(); + void organiseImports(CompilationUnit cu) { + List importDecls = new ArrayList<>(); + for (ImportDeclaration importDeclaration : cu.getImports()) { + String name = importDeclaration.getNameAsString(); + if (name.startsWith("org.jctools.util.Unsafe")) { continue; } + if (name.startsWith("org.jctools.queues.CircularArrayOffsetCalculator")) { + continue; + } + + if (name.startsWith("org.jctools.queues.LinkedArrayQueueUtil")) { + importDeclaration.setName(name.replace("org.jctools.queues.LinkedArrayQueueUtil", "org.jctools.queues.atomic.LinkedAtomicArrayQueueUtil")); + } + + importDecls.add(importDeclaration); + } + cu.getImports().clear(); + for (ImportDeclaration importDecl : importDecls) { + cu.addImport(importDecl); + } + cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicReferenceFieldUpdater")); + cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicLongFieldUpdater")); + cu.addImport(importDeclaration("org.jctools.queues.MessagePassingQueue")); + cu.addImport(importDeclaration("org.jctools.queues.MessagePassingQueue.Supplier")); + cu.addImport(importDeclaration("org.jctools.queues.MessagePassingQueueUtil")); + cu.addImport(importDeclaration("org.jctools.queues.QueueProgressIndicators")); + cu.addImport(importDeclaration("org.jctools.queues.IndexedQueueSizeUtil")); + cu.addImport(staticImportDeclaration("org.jctools.queues.atomic.LinkedAtomicArrayQueueUtil.*")); + cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicReferenceArray")); + cu.addImport(importDeclaration("org.jctools.queues.MpmcArrayQueue")); + } + + /** + * Given a variable declaration of some sort, check it's name and type and + * if it looks like any of the key type changes between unsafe and atomic + * queues, perform the conversion to change it's type. + * + * @param node + * @param name + */ + void processSpecialNodeTypes(NodeWithType node, String name) { + Type type = node.getType(); + if (node instanceof MethodDeclaration && ("newBufferAndOffset".equals(name) || "nextArrayOffset".equals(name))) { + node.setType(PrimitiveType.intType()); + } else if (PrimitiveType.longType().equals(type)) { + switch(name) { + case "offset": + case "offsetInNew": + case "offsetInOld": + case "lookAheadElementOffset": + node.setType(PrimitiveType.intType()); + } + } else if (isRefType(type, "LinkedQueueNode")) { + node.setType(simpleParametricType("LinkedQueueAtomicNode", "E")); + } else if (isRefArray(type, "E")) { + node.setType(atomicRefArrayType((ArrayType) type)); } } @@ -190,12 +209,12 @@ private static void removeStaticFieldsAndInitialisers(ClassOrInterfaceDeclaratio * * @param n */ - private static void replaceParentClassesForAtomics(ClassOrInterfaceDeclaration n) { + private void replaceParentClassesForAtomics(ClassOrInterfaceDeclaration n) { replaceParentClassesForAtomics(n.getExtendedTypes()); replaceParentClassesForAtomics(n.getImplementedTypes()); } - private static void replaceParentClassesForAtomics(NodeList types) { + private void replaceParentClassesForAtomics(NodeList types) { for (ClassOrInterfaceType parent : types) { if ("BaseLinkedQueue".equals(parent.getNameAsString())) { parent.setName("BaseLinkedAtomicQueue"); @@ -217,7 +236,7 @@ private static void replaceParentClassesForAtomics(NodeListP_INDEX_UPDATER.lazySet(this, newValue) - * - * @param fieldUpdaterFieldName - * @param newValueName - * @return - */ - private static 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 - * return P_INDEX_UPDATER.compareAndSet(this, expectedValue, newValue) - * - * @param fieldUpdaterFieldName - * @param expectedValueName - * @param newValueName - * @return - */ - private static 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; - } - /** * Generates something like * return P_INDEX_UPDATER.getAndSet(this, newValue) @@ -347,47 +316,13 @@ private static BlockStmt fieldUpdaterCompareAndSet(String fieldUpdaterFieldName, * @param newValueName * @return */ - private static BlockStmt fieldUpdaterGetAndSet(String fieldUpdaterFieldName, String newValueName) { + private BlockStmt fieldUpdaterGetAndSet(String fieldUpdaterFieldName, String newValueName) { BlockStmt body = new BlockStmt(); body.addStatement(new ReturnStmt( methodCallExpr(fieldUpdaterFieldName, "getAndSet", new ThisExpr(), new NameExpr(newValueName)))); return body; } - /** - * Generates something like field = newValue - * - * @param fieldName - * @param valueName - * @return - */ - private static 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 - * private static final AtomicLongFieldUpdater P_INDEX_UPDATER = AtomicLongFieldUpdater.newUpdater(MpmcAtomicArrayQueueProducerIndexField.class, "producerIndex"); - * - * @param type - * @param name - * @param initializer - * @param modifiers - * @return - */ - private static FieldDeclaration fieldDeclarationWithInitialiser(Type type, String name, Expression initializer, - Modifier... modifiers) { - FieldDeclaration fieldDeclaration = new FieldDeclaration(); - VariableDeclarator variable = new VariableDeclarator(type, name, initializer); - fieldDeclaration.getVariables().add(variable); - EnumSet modifierSet = EnumSet.copyOf(Arrays.asList(modifiers)); - fieldDeclaration.setModifiers(modifierSet); - return fieldDeclaration; - } - /** * Generates something like * private static final AtomicReferenceFieldUpdater P_NODE_UPDATER = AtomicReferenceFieldUpdater.newUpdater(MpmcAtomicArrayQueueProducerNodeField.class, "producerNode"); @@ -396,7 +331,7 @@ private static FieldDeclaration fieldDeclarationWithInitialiser(Type type, Strin * @param variableName * @return */ - private static FieldDeclaration declareRefFieldUpdater(String className, String variableName) { + private FieldDeclaration declareRefFieldUpdater(String className, String variableName) { MethodCallExpr initializer = newAtomicRefFieldUpdater(className, variableName); ClassOrInterfaceType type = simpleParametricType("AtomicReferenceFieldUpdater", className, @@ -406,174 +341,26 @@ private static FieldDeclaration declareRefFieldUpdater(String className, String return newField; } - private static MethodCallExpr newAtomicRefFieldUpdater(String className, String variableName) { + private MethodCallExpr newAtomicRefFieldUpdater(String className, String variableName) { return methodCallExpr("AtomicReferenceFieldUpdater", "newUpdater", new ClassExpr(classType(className)), new ClassExpr(classType("LinkedQueueAtomicNode")), new StringLiteralExpr(variableName)); } - /** - * Generates something like - * private static final AtomicLongFieldUpdater P_INDEX_UPDATER = AtomicLongFieldUpdater.newUpdater(MpmcAtomicArrayQueueProducerIndexField.class, "producerIndex"); - * - * @param className - * @param variableName - * @return - */ - private static FieldDeclaration declareLongFieldUpdater(String className, String variableName) { - MethodCallExpr initializer = newAtomicLongFieldUpdater(className, variableName); - - ClassOrInterfaceType type = simpleParametricType("AtomicLongFieldUpdater", className); - FieldDeclaration newField = fieldDeclarationWithInitialiser(type, fieldUpdaterFieldName(variableName), - initializer, Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL); - return newField; - } - - private static MethodCallExpr newAtomicLongFieldUpdater(String className, String variableName) { - return methodCallExpr("AtomicLongFieldUpdater", "newUpdater", new ClassExpr(classType(className)), - new StringLiteralExpr(variableName)); - } - - /** - * Generates something like return field - * - * @param fieldName - * @return - */ - private static BlockStmt returnField(String fieldName) { - BlockStmt body = new BlockStmt(); - body.addStatement(new ReturnStmt(fieldName)); - return body; - } - - private static boolean isRefArray(Type in, String refClassName) { - if (in instanceof ArrayType) { - ArrayType aType = (ArrayType) in; - return isRefType(aType.getComponentType(), refClassName); - } - return false; - } - - private static 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 ClassOrInterfaceType atomicRefArrayType(ArrayType in) { + private ClassOrInterfaceType atomicRefArrayType(ArrayType in) { ClassOrInterfaceType out = new ClassOrInterfaceType(null, "AtomicReferenceArray"); out.setTypeArguments(in.getComponentType()); return out; } - private static 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; - } - - private static ClassOrInterfaceType simpleParametricType(String className, String... typeArgs) { - NodeList typeArguments = new NodeList(); - for (String typeArg : typeArgs) { - typeArguments.add(classType(typeArg)); - } - return new ClassOrInterfaceType(null, new SimpleName(className), typeArguments); - } - - private static ClassOrInterfaceType classType(String className) { - return new ClassOrInterfaceType(null, className); - } - - private static ImportDeclaration importDeclaration(String name) { - return new ImportDeclaration(new Name(name), false, false); - } - - private static ImportDeclaration staticImportDeclaration(String name) { + private ImportDeclaration staticImportDeclaration(String name) { return new ImportDeclaration(new Name(name), true, false); } - private static String translateQueueName(String originalQueueName) { - if (originalQueueName.length() < 5) { - return originalQueueName; - } - - if (originalQueueName.contains("LinkedQueue") || originalQueueName.contains("LinkedArrayQueue")) { - return originalQueueName.replace("Linked", "LinkedAtomic"); - } - - if (originalQueueName.contains("ArrayQueue")) { - return originalQueueName.replace("ArrayQueue", "AtomicArrayQueue"); - } - - return originalQueueName; - } - - private static String fieldUpdaterFieldName(String fieldName) { - switch (fieldName) { - case "producerNode": - return "P_NODE_UPDATER"; - case "consumerNode": - return "C_NODE_UPDATER"; - case "consumerIndex": - return "C_INDEX_UPDATER"; - case "producerIndex": - return "P_INDEX_UPDATER"; - case "producerLimit": - return "P_LIMIT_UPDATER"; - default: - throw new IllegalArgumentException("Unhandled field: " + fieldName); - } - } - - private static void organiseImports(CompilationUnit cu) { - List importDecls = new ArrayList<>(); - for (ImportDeclaration importDeclaration : cu.getImports()) { - String name = importDeclaration.getNameAsString(); - if (name.startsWith("org.jctools.util.Unsafe")) { - continue; - } - if (name.startsWith("org.jctools.queues.CircularArrayOffsetCalculator")) { - continue; - } - - if (name.startsWith("org.jctools.queues.LinkedArrayQueueUtil")) { - importDeclaration.setName(name.replace("org.jctools.queues.LinkedArrayQueueUtil", "org.jctools.queues.atomic.LinkedAtomicArrayQueueUtil")); - } - - importDecls.add(importDeclaration); - } - cu.getImports().clear(); - for (ImportDeclaration importDecl : importDecls) { - cu.addImport(importDecl); - } - cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicReferenceFieldUpdater")); - cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicLongFieldUpdater")); - cu.addImport(importDeclaration("org.jctools.queues.MessagePassingQueue")); - cu.addImport(importDeclaration("org.jctools.queues.MessagePassingQueue.Supplier")); - cu.addImport(importDeclaration("org.jctools.queues.MessagePassingQueueUtil")); - cu.addImport(importDeclaration("org.jctools.queues.QueueProgressIndicators")); - cu.addImport(importDeclaration("org.jctools.queues.IndexedQueueSizeUtil")); - cu.addImport(staticImportDeclaration("org.jctools.queues.atomic.LinkedAtomicArrayQueueUtil.*")); - cu.addImport(importDeclaration("java.util.concurrent.atomic.AtomicReferenceArray")); - cu.addImport(importDeclaration("org.jctools.queues.MpmcArrayQueue")); - } - - private static void processSpecialNodeTypes(Parameter node) { - processSpecialNodeTypes(node, node.getNameAsString()); - } - - private static void processSpecialNodeTypes(VariableDeclarator node) { - processSpecialNodeTypes(node, node.getNameAsString()); - } - - private static void processSpecialNodeTypes(MethodDeclaration node) { + private void processSpecialNodeTypes(MethodDeclaration node) { processSpecialNodeTypes(node, node.getNameAsString()); } - private static void processSpecialNodeTypes(ObjectCreationExpr node) { + private void processSpecialNodeTypes(ObjectCreationExpr node) { Type type = node.getType(); if (isRefType(type, "LinkedQueueNode")) { node.setType(simpleParametricType("LinkedQueueAtomicNode", "E")); @@ -582,57 +369,4 @@ private static void processSpecialNodeTypes(ObjectCreationExpr node) { } } - /** - * Given a variable declaration of some sort, check it's name and type and - * if it looks like any of the key type changes between unsafe and atomic - * queues, perform the conversion to change it's type. - * - * @param node - * @param name - */ - private static void processSpecialNodeTypes(NodeWithType node, String name) { - Type type = node.getType(); - if (node instanceof MethodDeclaration && ("newBufferAndOffset".equals(name) || "nextArrayOffset".equals(name))) { - node.setType(PrimitiveType.intType()); - } else if (PrimitiveType.longType().equals(type)) { - switch(name) { - case "offset": - case "offsetInNew": - case "offsetInOld": - case "lookAheadElementOffset": - node.setType(PrimitiveType.intType()); - } - } else if (isRefType(type, "LinkedQueueNode")) { - node.setType(simpleParametricType("LinkedQueueAtomicNode", "E")); - } else if (isRefArray(type, "E")) { - node.setType(atomicRefArrayType((ArrayType) type)); - } - } - - public static void main(String[] args) throws Exception { - - if (args.length < 2) { - throw new IllegalArgumentException("Usage: outputDirectory inputSourceFiles"); - } - - File outputDirectory = new File(args[0]); - - for (int i = 1; i < args.length; i++) { - File file = new File(args[i]); - System.out.println("Processing " + file); - CompilationUnit cu = JavaParser.parse(file); - new JavaParsingAtomicLinkedQueueGenerator(file.getName()).visit(cu, null); - - organiseImports(cu); - - String outputFileName = translateQueueName(file.getName().replace(".java", "")) + ".java"; - - try (FileWriter writer = new FileWriter(new File(outputDirectory, outputFileName))) { - writer.write(cu.toString()); - } - - System.out.println("Saved to " + outputFileName); - } - } - } diff --git a/jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicQueueGenerator.java b/jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicQueueGenerator.java new file mode 100644 index 00000000..b332a6e2 --- /dev/null +++ b/jctools-build/src/main/java/org/jctools/queues/atomic/JavaParsingAtomicQueueGenerator.java @@ -0,0 +1,309 @@ +package org.jctools.queues.atomic; + +import java.io.File; +import java.io.FileWriter; +import java.util.Arrays; +import java.util.EnumSet; +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.Node; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.PackageDeclaration; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.FieldDeclaration; +import com.github.javaparser.ast.body.InitializerDeclaration; +import com.github.javaparser.ast.body.Parameter; +import com.github.javaparser.ast.body.VariableDeclarator; +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.FieldAccessExpr; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.expr.Name; +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; + +abstract class JavaParsingAtomicQueueGenerator extends VoidVisitorAdapter { + protected static final String INDENT_LEVEL = " "; + protected final String sourceFileName; + + static void main(Class generatorClass, String[] args) throws Exception { + + if (args.length < 2) { + throw new IllegalArgumentException("Usage: outputDirectory inputSourceFiles"); + } + + File outputDirectory = new File(args[0]); + + for (int i = 1; i < args.length; i++) { + File file = new File(args[i]); + System.out.println("Processing " + file); + CompilationUnit cu = JavaParser.parse(file); + JavaParsingAtomicQueueGenerator generator = buildGenerator(generatorClass, file.getName()); + generator.visit(cu, null); + + generator.organiseImports(cu); + + String outputFileName = generator.translateQueueName(file.getName().replace(".java", "")) + ".java"; + + try (FileWriter writer = new FileWriter(new File(outputDirectory, outputFileName))) { + writer.write(cu.toString()); + } + + System.out.println("Saved to " + outputFileName); + } + } + + JavaParsingAtomicQueueGenerator(String sourceFileName) { + this.sourceFileName = sourceFileName; + } + + abstract void organiseImports(CompilationUnit cu); + abstract String translateQueueName(String fileName); + abstract void processSpecialNodeTypes(NodeWithType node, String name); + abstract String fieldUpdaterFieldName(String fieldName); + + @Override + public void visit(FieldAccessExpr n, Void arg) { + super.visit(n, arg); + if (n.getScope() instanceof NameExpr) { + NameExpr name = (NameExpr) n.getScope(); + name.setName(translateQueueName(name.getNameAsString())); + } + } + + @Override + public void visit(PackageDeclaration n, Void arg) { + super.visit(n, arg); + // Change the package of the output + n.setName("org.jctools.queues.atomic"); + } + + @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 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.STATIC)) { + field.remove(); + continue; + } + } + } + + protected String capitalise(String s) { + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + protected String formatMultilineJavadoc(int indent, String... lines) { + String indentation = ""; + for (int i = 0; i < indent; i++) { + indentation += INDENT_LEVEL; + } + + String out = "\n"; + for (String line : lines) { + out += indentation + " * " + line + "\n"; + } + out += indentation + " "; + return out; + } + + /** + * Generates something like + * P_INDEX_UPDATER.lazySet(this, newValue) + * + * @param fieldUpdaterFieldName + * @param newValueName + * @return + */ + 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 + * return P_INDEX_UPDATER.compareAndSet(this, expectedValue, newValue) + * + * @param fieldUpdaterFieldName + * @param expectedValueName + * @param newValueName + * @return + */ + 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 field = newValue + * + * @param fieldName + * @param valueName + * @return + */ + 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 + * private static final AtomicLongFieldUpdater P_INDEX_UPDATER = AtomicLongFieldUpdater.newUpdater(MpmcAtomicArrayQueueProducerIndexField.class, "producerIndex"); + * + * @param type + * @param name + * @param initializer + * @param modifiers + * @return + */ + protected FieldDeclaration fieldDeclarationWithInitialiser(Type type, String name, Expression initializer, + Modifier... modifiers) { + FieldDeclaration fieldDeclaration = new FieldDeclaration(); + VariableDeclarator variable = new VariableDeclarator(type, name, initializer); + fieldDeclaration.getVariables().add(variable); + EnumSet modifierSet = EnumSet.copyOf(Arrays.asList(modifiers)); + fieldDeclaration.setModifiers(modifierSet); + return fieldDeclaration; + } + + /** + * Generates something like + * private static final AtomicLongFieldUpdater P_INDEX_UPDATER = AtomicLongFieldUpdater.newUpdater(MpmcAtomicArrayQueueProducerIndexField.class, "producerIndex"); + * + * @param className + * @param variableName + * @return + */ + protected FieldDeclaration declareLongFieldUpdater(String className, String variableName) { + MethodCallExpr initializer = newAtomicLongFieldUpdater(className, variableName); + + ClassOrInterfaceType type = simpleParametricType("AtomicLongFieldUpdater", className); + FieldDeclaration newField = fieldDeclarationWithInitialiser(type, fieldUpdaterFieldName(variableName), + initializer, Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL); + return newField; + } + + 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 typeArguments = new NodeList(); + 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); + } + + protected ImportDeclaration importDeclaration(String name) { + return new ImportDeclaration(new Name(name), false, false); + } + + /** + * Generates something like return field + * + * @param fieldName + * @return + */ + 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 buildGenerator(Class generatorClass, String fileName) throws Exception { + return generatorClass.getDeclaredConstructor(String.class).newInstance(fileName); + } + +}