Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memoize Annotations Added #3

Closed
wants to merge 53 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
7b0c411
attempting to add Memoize ast transformation... not quite working yet.
ctoestreich Dec 30, 2011
39c73bc
attempting to add Memoize ast transformation... not quite working yet.
ctoestreich Dec 31, 2011
993605b
making some progress... just need to solve the issue with getting var…
ctoestreich Jan 12, 2012
f7aff29
making some progress... just need to solve the issue with getting var…
ctoestreich Jan 12, 2012
33a7946
trying a bunch of things to get the variableScope to work....
ctoestreich Jan 12, 2012
d9f6a54
looked at using the VariableScopeVisitor to no avail. still produces…
ctoestreich Jan 13, 2012
bcb18d7
I think it is working finally...
ctoestreich Jan 13, 2012
86ad25c
adding spock and integration specs for testing simple and complex str…
ctoestreich Jan 13, 2012
00c3fe6
adding spock and integration specs for testing simple and complex str…
ctoestreich Jan 13, 2012
bfbaa33
adding ability to use closure or params also added expire to usable p…
ctoestreich Jan 14, 2012
c07d513
appending the method name to the key automatically
ctoestreich Jan 14, 2012
a3b6e51
fixing key generation string
ctoestreich Jan 14, 2012
701cc04
fixing key generation string
ctoestreich Jan 14, 2012
f405e1c
removing the method name from cache key
ctoestreich Jan 17, 2012
4c7acca
moving some folders and adding codenarc and coverage and tests to mai…
ctoestreich Jan 17, 2012
709aaf8
adding AST tests to the mainline code and adding ignore for files in …
ctoestreich Jan 17, 2012
a5c66d5
adding AST tests to the mainline code and adding ignore for files in …
ctoestreich Jan 17, 2012
8282130
missed adding AST when I moved :)
ctoestreich Jan 17, 2012
e035485
still having trouble parsing the gstrings
ctoestreich Jan 17, 2012
59838cc
still having trouble parsing the gstrings
ctoestreich Jan 17, 2012
c9fa260
finally got the gstrings to work and added list and has support. hav…
ctoestreich Jan 18, 2012
cc51b44
added remaining ASTs
ctoestreich Jan 19, 2012
52fe31f
doing a bunch of cleanup and adding detection and auto injection of t…
ctoestreich Jan 23, 2012
882bf9e
starting to update documentation, doing a bunch of cleanup and adding…
ctoestreich Jan 23, 2012
5130aa4
updating documentation
ctoestreich Jan 23, 2012
48358df
updating documentation
ctoestreich Jan 23, 2012
f8fef5a
updating documentation
ctoestreich Jan 23, 2012
6ad5f7c
updating documentation
ctoestreich Jan 23, 2012
fe5eef5
updating documentation
ctoestreich Jan 30, 2012
2f8b498
updating documentation
ctoestreich Jan 30, 2012
00309fc
updating documentation
ctoestreich Jan 30, 2012
a29a3ea
updating documentation
ctoestreich Jan 30, 2012
bf481da
updating documentation
ctoestreich Jan 30, 2012
a0d147c
adding domain and service for test and adding the files in plugin exc…
ctoestreich Jan 30, 2012
159c9a3
codenarc cleanup a bit of the service code
ctoestreich Jan 30, 2012
3de6f9d
codenarc cleanup
ctoestreich Jan 30, 2012
43247ba
codenarc cleanup
ctoestreich Jan 30, 2012
01242f5
codenarc report naming
ctoestreich Jan 30, 2012
a987081
codenarc report naming
ctoestreich Jan 30, 2012
ff9515c
codenarc cleanup
ctoestreich Jan 30, 2012
1e8a928
codenarc cleanup
ctoestreich Jan 30, 2012
405c8b9
fixing build for default project
ctoestreich Jan 30, 2012
50bdef7
fixing build for default project
ctoestreich Jan 30, 2012
4d4f0bf
fixing build for default project
ctoestreich Jan 30, 2012
020deb2
codenarc cleanup
ctoestreich Jan 30, 2012
05c08d9
codenarc cleanup
ctoestreich Jan 30, 2012
649f603
codenarc cleanup
ctoestreich Jan 30, 2012
b23a6e2
codenarc cleanup
ctoestreich Jan 30, 2012
f11c7fa
codenarc cleanup
ctoestreich Jan 30, 2012
32aea5f
codenarc cleanup
ctoestreich Jan 30, 2012
bec3497
codenarc cleanup
ctoestreich Jan 30, 2012
a306006
codenarc cleanup
ctoestreich Jan 30, 2012
46f8625
removal of tomcat/hibernate from application.properties.
ctoestreich Feb 1, 2012
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
missed adding AST when I moved :)
  • Loading branch information
ctoestreich committed Jan 17, 2012
commit 828213091ff30edfab3bdb2c622542f2fc8fa5ae
18 changes: 18 additions & 0 deletions src/groovy/grails/plugin/redis/Memoize.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package grails.plugin.redis

import org.codehaus.groovy.transform.GroovyASTTransformationClass
import java.lang.annotation.ElementType
import java.lang.annotation.Target
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Retention

/**
*/
@Retention(RetentionPolicy.SOURCE)
@Target([ElementType.METHOD])
@GroovyASTTransformationClass(["grails.plugin.redis.MemoizeASTTransformation"])
public @interface Memoize {
Class value() default {true};
String key() default 'bad:key';
String expire() default '';
}
154 changes: 154 additions & 0 deletions src/groovy/grails/plugin/redis/MemoizeASTTransformation.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package grails.plugin.redis

import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.Parameter
import org.codehaus.groovy.ast.VariableScope
import org.codehaus.groovy.ast.stmt.BlockStatement
import org.codehaus.groovy.ast.stmt.ExpressionStatement
import org.codehaus.groovy.ast.stmt.ReturnStatement
import org.codehaus.groovy.ast.stmt.Statement
import org.codehaus.groovy.classgen.VariableScopeVisitor
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.control.messages.SyntaxErrorMessage
import org.codehaus.groovy.syntax.SyntaxException
import org.codehaus.groovy.transform.ASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformation
import org.codehaus.groovy.ast.expr.*

@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
class MemoizeASTTransformation implements ASTTransformation {

private static final String KEY = 'key'
private static final String EXPIRE = 'expire'

void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
MethodNode methodNode = (MethodNode) astNodes[1]
//def keyExpression = astNodes[0]?.members?.value

def memoizeProperties = [:]
generateMemoizeProperties(astNodes, sourceUnit, memoizeProperties)
if(!memoizeProperties.containsKey(KEY) || !memoizeProperties.get(KEY)) {
return
}

def stmt = memoizeMethod(methodNode, memoizeProperties)
methodNode.code.statements.clear()
methodNode.code.statements.addAll(stmt)

VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(sourceUnit);
sourceUnit.AST.classes.each {
scopeVisitor.visitClass(it)
}
}

/**
* method to add the key and expires and options if they exist
* @param astNodes the ast nodes
* @param sourceUnit the source unit
* @param memoizeProperties map to put data in
* @return
*/
private Map generateMemoizeProperties(ASTNode[] astNodes, SourceUnit sourceUnit, Map memoizeProperties) {
def expire = astNodes[0]?.members?.expire?.text
def keyString = astNodes[0]?.members?.key?.text
def keyClosure = astNodes[0]?.members?.value

//do some validation on the key for closure or key property ****************
if(keyClosure?.class != ClosureExpression && keyString.class != String) {
addError("Internal Error: annotation doesn't contain key closure or key property", astNodes[0], sourceUnit)
return
}

if(keyClosure && keyClosure.code?.statements[0]?.expression?.value?.class != String) {
addError("Internal Error: annotation doesn't contain string key closure", astNodes[0], sourceUnit)
return
}

if(expire && expire.class != String && !Integer.parseInt(expire)) {
addError("Internal Error: provided expire is not an String (in millis)", astNodes[0], sourceUnit)
return
}
//***************************************************************************

memoizeProperties.put(KEY, (keyClosure) ? keyClosure?.code?.statements[0]?.expression?.value : keyString)
if(expire) {
memoizeProperties.put(EXPIRE, expire)
}
}

private List<Statement> memoizeMethod(MethodNode methodNode, Map memoizeProperties) {
BlockStatement body = new BlockStatement()

// todo: remove this call after development
createInterceptionLogging(body, 'memoized method')

createRedisServiceMemoizeInvocation(body, methodNode, memoizeProperties)
return body.statements
}

/**
* this is just used for debugging during development
* todo: remove this after all things are flushed out
* @param body
*/
private void createInterceptionLogging(BlockStatement body, String message) {
body.addStatement(
new ExpressionStatement(
new MethodCallExpression(
new VariableExpression("this"),
new ConstantExpression("println"),
new ArgumentListExpression(
new ConstantExpression(message)
)
)
)
)
}

private void createRedisServiceMemoizeInvocation(BlockStatement body, MethodNode methodNode, Map memoizeProperties) {

//todo: refactor this to new method? *************************
ArgumentListExpression argumentListExpression = new ArgumentListExpression()
argumentListExpression.addExpression(createConstantExpression(methodNode, memoizeProperties.get(KEY).toString().replace('#', '\$')))
if(memoizeProperties.containsKey(EXPIRE)){
argumentListExpression.addExpression(createConstantExpression(methodNode, Integer.parseInt(memoizeProperties.get(EXPIRE).toString())))
}
argumentListExpression.addExpression(createClosureExpression(methodNode))
//**************************************************************

body.addStatement(
new ReturnStatement(
new MethodCallExpression(
new VariableExpression("redisService"),
new ConstantExpression("memoize"),
argumentListExpression
)
)
)
}

private ClosureExpression createClosureExpression(MethodNode methodNode) {

ClosureExpression closureExpression = new ClosureExpression(
[] as Parameter[],
new BlockStatement(methodNode.code.statements as Statement[], new VariableScope())
)
closureExpression.variableScope = methodNode.variableScope.copy()
return closureExpression
}

//todo generate a better key here
private ConstantExpression createConstantExpression(MethodNode methodNode, constantExpression) {
return new ConstantExpression(constantExpression)
}

public void addError(String msg, ASTNode node, SourceUnit source) {
int line = node.lineNumber
int col = node.columnNumber
SyntaxException se = new SyntaxException(msg + '\n', line, col)
SyntaxErrorMessage sem = new SyntaxErrorMessage(se, source)
source.errorCollector.addErrorAndContinue(sem)
}
}