232 lines
7.9 KiB
Groovy
232 lines
7.9 KiB
Groovy
package net.minecraftforge.forge.tasks
|
|
|
|
import java.util.ArrayList
|
|
import java.util.TreeMap
|
|
|
|
import org.gradle.api.DefaultTask
|
|
import org.gradle.api.tasks.InputFile
|
|
import org.gradle.api.tasks.InputFiles
|
|
import org.gradle.api.tasks.TaskAction
|
|
import org.objectweb.asm.Opcodes
|
|
|
|
public class CheckATs extends DefaultTask {
|
|
@InputFile File inheritance
|
|
@InputFiles File[] ats
|
|
|
|
@TaskAction
|
|
protected void exec() {
|
|
Util.init()
|
|
def parse = { line ->
|
|
def idx = line.indexOf('#')
|
|
def comment = idx == -1 ? null : line.substring(idx)
|
|
if (idx != -1) line = line.substring(0, idx - 1)
|
|
def (modifier, cls, desc) = (line.trim() + ' ').split(' ', -1)
|
|
def key = cls + (desc.isEmpty() ? '' : ' ' + desc)
|
|
return [modifier, cls, desc, comment, key]
|
|
}
|
|
def accessLevel = { access ->
|
|
if ((access & Opcodes.ACC_PUBLIC) != 0) return 3
|
|
if ((access & Opcodes.ACC_PROTECTED) != 0) return 2
|
|
if ((access & Opcodes.ACC_PRIVATE) != 0) return 0
|
|
return 1
|
|
}
|
|
def accessStr = { access ->
|
|
if (access.endsWith('-f') || access.endsWith('+f'))
|
|
return 4
|
|
switch (access.toLowerCase()) {
|
|
case 'public': return 3
|
|
case 'protected': return 2
|
|
case 'default': return 1
|
|
case 'private': return 0
|
|
default: return -1
|
|
}
|
|
}
|
|
def json = inheritance.json()
|
|
|
|
ats.each { f ->
|
|
TreeMap lines = [:]
|
|
def group = null
|
|
for (def line : f.readLines()) {
|
|
if (line.isEmpty()) continue
|
|
if (line.startsWith('#group ')) {
|
|
def (modifier, cls, desc, comment, key) = parse.call(line.substring(7))
|
|
|
|
if (!desc.equals('*') && !desc.equals('*()') && !desc.equals('<init>'))
|
|
throw new IllegalStateException('Invalid group: ' + line)
|
|
|
|
group = [modifier: modifier, cls: cls, desc: desc, comment: comment,
|
|
'existing': [] as Set,
|
|
'children': [] as TreeSet,
|
|
group: true
|
|
]
|
|
if (lines.containsKey(key))
|
|
throw new IllegalStateException('Duplicate group: ' + line)
|
|
|
|
lines[key] = group
|
|
} else if (group != null) {
|
|
if (line.startsWith('#endgroup')) {
|
|
group = null
|
|
} else {
|
|
def (modifier, cls, desc, comment, key) = parse.call(line)
|
|
group['existing'].add(key)
|
|
}
|
|
} else if (line.startsWith('#endgroup')) {
|
|
throw new IllegalStateException('Invalid group ending: ' + line)
|
|
} else if (line.startsWith('#')) {
|
|
//Nom
|
|
} else {
|
|
def (modifier, cls, desc, comment, key) = parse.call(line)
|
|
if (lines.containsKey(key)) {
|
|
println('Duplicate: ' + line)
|
|
continue
|
|
}
|
|
lines[key] = [modifier: modifier, cls: cls, desc: desc, comment: comment, group: false]
|
|
}
|
|
}
|
|
|
|
// Process Groups, this will remove any entries outside the group that is covered by the group
|
|
for (def key : new ArrayList<>(lines.keySet())) {
|
|
def entry = lines.get(key)
|
|
if (entry != null && entry['group']) {
|
|
def cls = entry['cls']
|
|
def jcls = json.get(cls.replaceAll('\\.', '/'))
|
|
if (jcls == null) {
|
|
lines.remove(key)
|
|
println('Invalid Group: ' + key)
|
|
} else if ('*'.equals(entry['desc'])) {
|
|
if (!jcls.containsKey('fields')) {
|
|
lines.remove(key)
|
|
println('Invalid Group, Class has no fields: ' + key)
|
|
} else {
|
|
jcls['fields'].each { field, value ->
|
|
def fkey = cls + ' ' + field
|
|
if (accessLevel.call(value['access']) < accessStr.call(entry['modifier'])) {
|
|
if (lines.containsKey(fkey)) {
|
|
lines.remove(fkey)
|
|
} else if (!entry['existing'].contains(fkey)) {
|
|
println('Added: ' + fkey)
|
|
}
|
|
entry['children'].add(fkey)
|
|
} else if (lines.containsKey(fkey)) {
|
|
lines.remove(fkey)
|
|
println('Removed: ' + fkey)
|
|
}
|
|
}
|
|
entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) }
|
|
}
|
|
} else if ('*()'.equals(entry['desc'])) {
|
|
if (!jcls.containsKey('methods')) {
|
|
lines.remove(key)
|
|
println('Invalid Group, Class has no methods: ' + key)
|
|
} else {
|
|
jcls['methods'].each{ mtd, value ->
|
|
if (mtd.startsWith('<clinit>'))
|
|
return
|
|
key = cls + ' ' + mtd.replace(' ', '')
|
|
if (accessLevel.call(value['access']) < accessStr.call(entry['modifier'])) {
|
|
if (lines.containsKey(key)) {
|
|
lines.remove(key)
|
|
} else if (!entry['existing'].contains(key)) {
|
|
println('Added: ' + key)
|
|
}
|
|
entry['children'].add(key)
|
|
} else if (lines.containsKey(key)) {
|
|
lines.remove(key)
|
|
println('Removed: ' + key)
|
|
}
|
|
}
|
|
entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) }
|
|
}
|
|
} else if ('<init>'.equals(entry['desc'])) { //Make all public non-abstract subclasses
|
|
json.each{ tcls,value ->
|
|
if (!value.containsKey('methods') || ((value['access'] & Opcodes.ACC_ABSTRACT) != 0))
|
|
return
|
|
def parents = [] as Set
|
|
def parent = tcls
|
|
while (parent != null && json.containsKey(parent)) {
|
|
parents.add(parent)
|
|
def p = json[parent]
|
|
parent = p == null ? null : p['superName']
|
|
}
|
|
if (parents.contains(cls.replaceAll('\\.', '/'))) {
|
|
value['methods'].each{ mtd, v ->
|
|
if (mtd.startsWith('<init>')) {
|
|
def child = tcls.replaceAll('/', '\\.') + ' ' + mtd.replace(' ', '')
|
|
if (accessLevel.call(v['access']) < 3) {
|
|
if (lines.containsKey(child)) {
|
|
lines.remove(child)
|
|
} else if (!entry['existing'].contains(child)) {
|
|
println('Added: ' + child)
|
|
}
|
|
entry['children'].add(child)
|
|
} else if (lines.containsKey(child)) {
|
|
lines.remove(child)
|
|
println('Removed: ' + child)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) }
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process normal lines, remove invalid and remove narrowing
|
|
for (def key : new ArrayList<>(lines.keySet())) {
|
|
def entry = lines.get(key)
|
|
if (entry != null && !entry['group']) {
|
|
def cls = entry['cls']
|
|
def jcls = json.get(cls.replaceAll('\\.', '/'))
|
|
if (jcls == null) {
|
|
lines.remove(key)
|
|
println('Invalid: ' + key)
|
|
} else if (entry['desc'] == '') {
|
|
if (accessLevel.call(jcls['access']) >= accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) {
|
|
lines.remove(key)
|
|
println('Invalid Narrowing: ' + key)
|
|
}
|
|
} else if (!entry['desc'].contains('(')) {
|
|
if (!jcls.containsKey('fields') || !jcls['fields'].containsKey(entry['desc'])) {
|
|
lines.remove(key)
|
|
println('Invalid: ' + key)
|
|
} else {
|
|
def value = jcls['fields'][entry['desc']]
|
|
if (accessLevel.call(value['access']) >= accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) {
|
|
lines.remove(key)
|
|
println('Invalid Narrowing: ' + key)
|
|
println(entry.comment)
|
|
}
|
|
}
|
|
} else {
|
|
def jdesc = entry['desc'].replace('(', ' (')
|
|
if (!jcls.containsKey('methods') || !jcls['methods'].containsKey(jdesc)) {
|
|
lines.remove(key)
|
|
println('Invalid: ' + key)
|
|
} else {
|
|
def value = jcls['methods'][jdesc]
|
|
if (accessLevel.call(value['access']) >= accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) {
|
|
lines.remove(key)
|
|
println('Invalid Narrowing: ' + key)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
def data = []
|
|
lines.each { key,value ->
|
|
if (!value.group) {
|
|
data.add(value.modifier + ' ' + key + (value.comment == null ? '' : ' ' + value.comment))
|
|
} else {
|
|
data.add('#group ' + value.modifier + ' ' + key + (value.comment == null ? '' : ' ' + value.comment))
|
|
value.children.each{ data.add(value.modifier + ' ' + it) }
|
|
data.add('#endgroup')
|
|
}
|
|
}
|
|
f.text = data.join('\n')
|
|
}
|
|
}
|
|
}
|