in subprojects/groovy-templates/src/main/groovy/groovy/text/markup/MarkupTemplateTypeCheckingExtension.groovy [61:171]
Object run() {
def modelTypesClassNodes = null
beforeVisitClass { classNode ->
def modelTypes = MarkupTemplateEngine.TemplateGroovyClassLoader.modelTypes.get()
if (modelTypes != null) {
modelTypesClassNodes = [:]
modelTypes.each { k, v ->
modelTypesClassNodes[k] = buildNodeFromString(v, context)
}
}
def modelTypesFromTemplate = classNode.getNodeMetaData(MarkupTemplateEngine.MODELTYPES_ASTKEY)
if (modelTypesFromTemplate) {
if (modelTypesClassNodes == null) {
modelTypesClassNodes = modelTypesFromTemplate
} else {
modelTypesClassNodes.putAll(modelTypesFromTemplate)
}
}
if (modelTypesClassNodes == null) {
// push a new error collector, we want type checking errors to be silent
context.pushErrorCollector()
}
}
beforeVisitMethod {
newScope {
builderCalls = []
binaryExpressions = [:]
}
}
methodNotFound { receiver, name, argList, argTypes, call ->
if ('getAt' == name && OBJECT_TYPE == receiver) {
// GROOVY-6940
def enclosingBinaryExpression = context.enclosingBinaryExpression
if (enclosingBinaryExpression.leftExpression.is(call.objectExpression)) {
def stack = context.enclosingBinaryExpressionStack
if (stack.size() > 1) {
def superEnclosing = stack.get(1)
def opType = superEnclosing.operation.type
if (superEnclosing.leftExpression.is(enclosingBinaryExpression) && isAssignment(opType)) {
if (opType == Types.ASSIGN) {
// type checker looks for getAt() but we need to replace the super binary expression with a putAt
// foo[x] = y --> foo.putAt(x,y)
def mce = new MethodCallExpression(
enclosingBinaryExpression.leftExpression,
'putAt',
new ArgumentListExpression(enclosingBinaryExpression.rightExpression, superEnclosing.rightExpression))
makeDynamic(mce)
currentScope.binaryExpressions.put(superEnclosing, mce)
return null
}
throw new UnsupportedOperationException("Operation not supported in templates: ${superEnclosing.text}. Please declare an explicit type for the variable.")
}
}
currentScope.binaryExpressions.put(enclosingBinaryExpression, call)
return makeDynamic(call)
}
}
if (call.lineNumber > 0) {
if (call.implicitThis) {
currentScope.builderCalls << call
return makeDynamic(call, OBJECT_TYPE)
}
if (modelTypesClassNodes == null) {
// unchecked mode
return makeDynamic(call, OBJECT_TYPE)
}
}
}
onMethodSelection { call, node ->
if (isMethodCallExpression(call) && modelTypesClassNodes != null) {
def args = getArguments(call).expressions
if (args.size() == 1) {
String varName = isConstantExpression(args[0]) ? args[0].text : call.getNodeMetaData(MarkupBuilderCodeTransformer.TARGET_VARIABLE)
def type = modelTypesClassNodes[varName]
if (type) {
if (call.objectExpression.text == 'this.getModel()') {
storeType(call, type)
} else if (call.methodAsString == 'tryEscape') {
storeType(call, type)
}
}
}
}
}
unresolvedProperty { pexp ->
if (pexp.objectExpression.text == 'this.getModel()') {
if (modelTypesClassNodes != null) {
// type checked mode detected!
def type = modelTypesClassNodes[pexp.propertyAsString]
if (type) {
makeDynamic(pexp, type)
}
} else {
makeDynamic(pexp)
}
} else if (modelTypesClassNodes == null) {
// dynamic mode
makeDynamic(pexp)
}
}
afterVisitMethod { mn ->
scopeExit {
new BuilderMethodReplacer(context.source, builderCalls, binaryExpressions).visitMethod(mn)
}
}
}