mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge pull request #5943 from joefarebrother/java-stub
[Java] Add stubbing script
This commit is contained in:
47
java/ql/src/utils/MinimalStubsFromSource.ql
Normal file
47
java/ql/src/utils/MinimalStubsFromSource.ql
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Tool to generate java stubs from a qltest snapshot.
|
||||
*
|
||||
* It finds all declarations used in the source code,
|
||||
* and generates minimal java stubs containing those declarations
|
||||
* and their dependencies.
|
||||
*/
|
||||
|
||||
import java
|
||||
import Stubs
|
||||
|
||||
/** Declarations used by source code. */
|
||||
class UsedInSource extends GeneratedDeclaration {
|
||||
UsedInSource() {
|
||||
(
|
||||
this = any(Variable v | v.fromSource()).getType()
|
||||
or
|
||||
this = any(Expr e | e.getEnclosingCallable().fromSource()).getType()
|
||||
or
|
||||
this = any(RefType t | t.fromSource())
|
||||
or
|
||||
this = any(TypeAccess ta | ta.fromSource())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from GeneratedTopLevel t
|
||||
where not t.fromSource()
|
||||
select t.getQualifiedName(), t.stubFile()
|
||||
|
||||
module Consistency {
|
||||
query predicate noGeneratedStubs(string s) {
|
||||
exists(GeneratedTopLevel t | s = t.getQualifiedName() |
|
||||
not t.fromSource() and
|
||||
not exists(t.stubFile())
|
||||
)
|
||||
}
|
||||
|
||||
query predicate multipleGeneratedStubs(string s) {
|
||||
exists(GeneratedTopLevel t | s = t.getQualifiedName() |
|
||||
not t.fromSource() and
|
||||
strictcount(t.stubFile()) > 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Consistency
|
||||
478
java/ql/src/utils/Stubs.qll
Normal file
478
java/ql/src/utils/Stubs.qll
Normal file
@@ -0,0 +1,478 @@
|
||||
/**
|
||||
* Generates java stubs for use in test code.
|
||||
*
|
||||
* Extend the abstract class `GeneratedDeclaration` with the declarations that should be generated.
|
||||
* This will generate stubs for all the required dependencies as well.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/** A type that should be in the generated code. */
|
||||
abstract private class GeneratedType extends RefType {
|
||||
GeneratedType() {
|
||||
(
|
||||
this instanceof Interface
|
||||
or
|
||||
this instanceof Class
|
||||
) and
|
||||
not this instanceof AnonymousClass and
|
||||
not this instanceof LocalClass and
|
||||
not this.getPackage() instanceof ExcludedPackage
|
||||
}
|
||||
|
||||
private string stubKeyword() {
|
||||
this instanceof Interface and result = "interface"
|
||||
or
|
||||
this instanceof Class and
|
||||
(if this instanceof EnumType then result = "enum" else result = "class")
|
||||
}
|
||||
|
||||
private string stubAbstractModifier() {
|
||||
if this.(Class).isAbstract() then result = "abstract " else result = ""
|
||||
}
|
||||
|
||||
private string stubStaticModifier() {
|
||||
if this.isStatic() then result = "static " else result = ""
|
||||
}
|
||||
|
||||
private string stubAccessibilityModifier() {
|
||||
if this.isPublic() then result = "public " else result = ""
|
||||
}
|
||||
|
||||
/** Gets the entire Java stub code for this type. */
|
||||
final string getStub() {
|
||||
result =
|
||||
this.stubAbstractModifier() + this.stubStaticModifier() + this.stubAccessibilityModifier() +
|
||||
this.stubKeyword() + " " + this.getName() + stubGenericArguments(this, true) +
|
||||
stubBaseTypesString() + "\n{\n" + stubMembers() + "}"
|
||||
}
|
||||
|
||||
private RefType getAnInterestingBaseType() {
|
||||
result = this.getASupertype() and
|
||||
not result instanceof TypeObject and
|
||||
not this instanceof EnumType and
|
||||
// generic types have their source declaration (the corresponding raw type) as a supertype of themselves
|
||||
result.getSourceDeclaration() != this
|
||||
}
|
||||
|
||||
private string stubBaseTypesString() {
|
||||
if exists(getAnInterestingBaseType())
|
||||
then
|
||||
exists(string cls, string interface, string int_kw | result = cls + int_kw + interface |
|
||||
(
|
||||
if exists(getAnInterestingBaseType().(Class))
|
||||
then cls = " extends " + stubTypeName(getAnInterestingBaseType().(Class))
|
||||
else cls = ""
|
||||
) and
|
||||
(
|
||||
if exists(getAnInterestingBaseType().(Interface))
|
||||
then (
|
||||
(if this instanceof Class then int_kw = " implements " else int_kw = " extends ") and
|
||||
interface = concat(stubTypeName(getAnInterestingBaseType().(Interface)), ", ")
|
||||
) else (
|
||||
int_kw = "" and interface = ""
|
||||
)
|
||||
)
|
||||
)
|
||||
else result = ""
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private string stubMembers() {
|
||||
result =
|
||||
stubEnumConstants(this) + stubFakeConstructor(this) +
|
||||
concat(Member m | m = this.getAGeneratedMember() | stubMember(m))
|
||||
}
|
||||
|
||||
private Member getAGeneratedMember() {
|
||||
(
|
||||
not result instanceof NestedType and
|
||||
result.getDeclaringType() = this
|
||||
or
|
||||
exists(NestedType nt | result = nt |
|
||||
nt = nt.getSourceDeclaration() and
|
||||
nt.getEnclosingType().getSourceDeclaration() = this
|
||||
)
|
||||
) and
|
||||
not result.isPrivate() and
|
||||
not result.isPackageProtected() and
|
||||
not result instanceof StaticInitializer and
|
||||
not result instanceof InstanceInitializer
|
||||
}
|
||||
|
||||
final Type getAGeneratedType() {
|
||||
result = getAnInterestingBaseType()
|
||||
or
|
||||
result = getAGeneratedMember().(Callable).getReturnType()
|
||||
or
|
||||
result = getAGeneratedMember().(Callable).getAParameter().getType()
|
||||
or
|
||||
result = getAGeneratedMember().(Field).getType()
|
||||
or
|
||||
result = getAGeneratedMember().(NestedType)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A declaration that should be generated.
|
||||
* This is extended in client code to identify the actual
|
||||
* declarations that should be generated.
|
||||
*/
|
||||
abstract class GeneratedDeclaration extends Element { }
|
||||
|
||||
private class IndirectType extends GeneratedType {
|
||||
IndirectType() {
|
||||
this.getASubtype() instanceof GeneratedType
|
||||
or
|
||||
this.(GenericType).getAParameterizedType() instanceof GeneratedType
|
||||
or
|
||||
exists(GeneratedType t |
|
||||
this = getAContainedType(t.getAGeneratedType()).(RefType).getSourceDeclaration()
|
||||
)
|
||||
or
|
||||
this.getSourceDeclaration() instanceof GeneratedType
|
||||
or
|
||||
this = any(GeneratedType t).getSourceDeclaration()
|
||||
or
|
||||
exists(GeneratedType t | this = t.(BoundedType).getATypeBound().getType())
|
||||
or
|
||||
exists(GeneratedDeclaration decl |
|
||||
decl.(Member).getDeclaringType().getSourceDeclaration() = this
|
||||
)
|
||||
or
|
||||
this.(NestedType).getEnclosingType() instanceof GeneratedType
|
||||
or
|
||||
exists(NestedType nt | nt instanceof GeneratedType and this = nt.getEnclosingType())
|
||||
or
|
||||
this = any(GeneratedType a).(Array).getComponentType()
|
||||
}
|
||||
}
|
||||
|
||||
private class RootGeneratedType extends GeneratedType {
|
||||
RootGeneratedType() { this = any(GeneratedDeclaration decl).(RefType).getSourceDeclaration() }
|
||||
}
|
||||
|
||||
private Type getAContainedType(Type t) {
|
||||
result = t
|
||||
or
|
||||
result = getAContainedType(t.(ParameterizedType).getATypeArgument())
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify packages to exclude.
|
||||
* Do not generate any types from these packages.
|
||||
*/
|
||||
abstract class ExcludedPackage extends Package { }
|
||||
|
||||
/** Exclude types from the standard library. */
|
||||
private class DefaultLibs extends ExcludedPackage {
|
||||
DefaultLibs() { this.getName().matches(["java.%", "jdk.%", "sun.%"]) }
|
||||
}
|
||||
|
||||
private string stubAccessibility(Member m) {
|
||||
if m.getDeclaringType() instanceof Interface
|
||||
then result = ""
|
||||
else
|
||||
if m.isPublic()
|
||||
then result = "public "
|
||||
else
|
||||
if m.isProtected()
|
||||
then result = "protected "
|
||||
else
|
||||
if m.isPrivate()
|
||||
then result = "private "
|
||||
else
|
||||
if m.isPackageProtected()
|
||||
then result = ""
|
||||
else result = "unknown-accessibility"
|
||||
}
|
||||
|
||||
private string stubModifiers(Member m) {
|
||||
result = stubAccessibility(m) + stubStaticOrFinal(m) + stubAbstractOrDefault(m)
|
||||
}
|
||||
|
||||
private string stubStaticOrFinal(Member m) {
|
||||
if m.(Modifiable).isStatic()
|
||||
then result = "static "
|
||||
else
|
||||
if m.(Modifiable).isFinal()
|
||||
then result = "final "
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubAbstractOrDefault(Member m) {
|
||||
if m.getDeclaringType() instanceof Interface
|
||||
then if m.isDefault() then result = "default " else result = ""
|
||||
else
|
||||
if m.isAbstract()
|
||||
then result = "abstract "
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubTypeName(Type t) {
|
||||
if t instanceof PrimitiveType
|
||||
then result = t.getName()
|
||||
else
|
||||
if t instanceof VoidType
|
||||
then result = "void"
|
||||
else
|
||||
if t instanceof TypeVariable
|
||||
then result = t.getName()
|
||||
else
|
||||
if t instanceof Wildcard
|
||||
then result = "?" + stubTypeBound(t)
|
||||
else
|
||||
if t instanceof Array
|
||||
then result = stubTypeName(t.(Array).getComponentType()) + "[]"
|
||||
else
|
||||
if t instanceof ClassOrInterface
|
||||
then
|
||||
result =
|
||||
stubQualifier(t) + t.(RefType).getSourceDeclaration().getName() +
|
||||
stubGenericArguments(t, false)
|
||||
else result = "<error>"
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private string stubTypeBound(BoundedType t) {
|
||||
if not exists(t.getATypeBound())
|
||||
then result = ""
|
||||
else
|
||||
exists(string kw, string bounds | result = kw + bounds |
|
||||
(if t.(Wildcard).hasLowerBound() then kw = " super " else kw = " extends ") and
|
||||
bounds =
|
||||
concat(TypeBound b |
|
||||
b = t.getATypeBound()
|
||||
|
|
||||
stubTypeName(b.getType()), " & " order by b.getPosition()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string maybeStubTypeBound(BoundedType t, boolean typeVarBounds) {
|
||||
typeVarBounds = true and
|
||||
result = stubTypeBound(t)
|
||||
or
|
||||
typeVarBounds = false and
|
||||
result = ""
|
||||
}
|
||||
|
||||
private string stubQualifier(RefType t) {
|
||||
if t instanceof NestedType
|
||||
then
|
||||
exists(RefType et | et = t.(NestedType).getEnclosingType().getSourceDeclaration() |
|
||||
result = stubQualifier(et) + et.getName() + "."
|
||||
)
|
||||
else result = ""
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private string stubGenericArguments(RefType t, boolean typeVarBounds) {
|
||||
typeVarBounds = [true, false] and
|
||||
if t instanceof GenericType
|
||||
then
|
||||
result =
|
||||
"<" +
|
||||
concat(int n, TypeVariable tv |
|
||||
tv = t.(GenericType).getTypeParameter(n)
|
||||
|
|
||||
tv.getName() + maybeStubTypeBound(tv, typeVarBounds), ", " order by n
|
||||
) + ">"
|
||||
else
|
||||
if t instanceof ParameterizedType
|
||||
then
|
||||
result =
|
||||
"<" +
|
||||
concat(int n, Type tpar |
|
||||
tpar = t.(ParameterizedType).getTypeArgument(n)
|
||||
|
|
||||
stubTypeName(tpar), ", " order by n
|
||||
) + ">"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubGenericCallableParams(Callable m) {
|
||||
if m instanceof GenericCallable
|
||||
then
|
||||
result =
|
||||
"<" +
|
||||
concat(int n, TypeVariable param |
|
||||
param = m.(GenericCallable).getTypeParameter(n)
|
||||
|
|
||||
param.getName() + stubTypeBound(param), ", " order by n
|
||||
) + "> "
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubImplementation(Callable c) {
|
||||
if c.isAbstract()
|
||||
then result = ";"
|
||||
else
|
||||
if c instanceof Constructor or c.getReturnType() instanceof VoidType
|
||||
then result = "{}"
|
||||
else result = "{ return " + stubDefaultValue(c.getReturnType()) + "; }"
|
||||
}
|
||||
|
||||
private string stubDefaultValue(Type t) {
|
||||
if t instanceof RefType
|
||||
then result = "null"
|
||||
else
|
||||
if t instanceof CharacterType
|
||||
then result = "'0'"
|
||||
else
|
||||
if t instanceof BooleanType
|
||||
then result = "false"
|
||||
else
|
||||
if t instanceof NumericType
|
||||
then result = "0"
|
||||
else result = "<error>"
|
||||
}
|
||||
|
||||
private string stubParameters(Callable c) {
|
||||
result =
|
||||
concat(int i, Parameter param |
|
||||
param = c.getParameter(i)
|
||||
|
|
||||
stubParameter(param), ", " order by i
|
||||
)
|
||||
}
|
||||
|
||||
private string stubParameter(Parameter p) {
|
||||
exists(Type t, string suff | result = stubTypeName(t) + suff + " " + p.getName() |
|
||||
if p.isVarargs()
|
||||
then (
|
||||
t = p.getType().(Array).getComponentType() and
|
||||
suff = "..."
|
||||
) else (
|
||||
t = p.getType() and suff = ""
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string stubEnumConstants(RefType t) {
|
||||
if t instanceof EnumType
|
||||
then
|
||||
exists(EnumType et | et = t |
|
||||
result =
|
||||
" " + concat(EnumConstant c | c = et.getAnEnumConstant() | c.getName(), ", ") + ";\n"
|
||||
)
|
||||
else result = ""
|
||||
}
|
||||
|
||||
// Holds if the member is to be excluded from stubMember
|
||||
private predicate excludedMember(Member m) {
|
||||
m instanceof EnumConstant
|
||||
or
|
||||
m.(Method).getDeclaringType() instanceof EnumType and
|
||||
m.hasName(["values", "valueOf"]) and
|
||||
m.isStatic()
|
||||
}
|
||||
|
||||
private string stubMember(Member m) {
|
||||
if excludedMember(m)
|
||||
then result = ""
|
||||
else (
|
||||
result =
|
||||
" " + stubModifiers(m) + stubGenericCallableParams(m) +
|
||||
stubTypeName(m.(Method).getReturnType()) + " " + m.getName() + "(" + stubParameters(m) + ")"
|
||||
+ stubImplementation(m) + "\n"
|
||||
or
|
||||
m instanceof Constructor and
|
||||
result =
|
||||
" " + stubModifiers(m) + stubGenericCallableParams(m) + m.getName() + "(" +
|
||||
stubParameters(m) + ")" + stubImplementation(m) + "\n"
|
||||
or
|
||||
result =
|
||||
" " + stubModifiers(m) + stubTypeName(m.(Field).getType()) + " " + m.getName() + " = " +
|
||||
stubDefaultValue(m.(Field).getType()) + ";\n"
|
||||
or
|
||||
result = indent(m.(NestedType).(GeneratedType).getStub())
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[s]
|
||||
private string indent(string s) { result = " " + s.replaceAll("\n", "\n ") + "\n" }
|
||||
|
||||
// If a class's superclass doesn't have a no-arg constructor, then it won't compile when its constructor's bodies are stubbed
|
||||
// So we synthesise no-arg constructors for each generated type that doesn't have one.
|
||||
private string stubFakeConstructor(RefType t) {
|
||||
if not t instanceof Class
|
||||
then result = ""
|
||||
else
|
||||
exists(string mod |
|
||||
// this won't conflict with any existing private constructors, since we don't generate stubs for any private members.
|
||||
if t instanceof EnumType then mod = " private " else mod = " protected "
|
||||
|
|
||||
if hasNoArgConstructor(t) then result = "" else result = mod + t.getName() + "() {}\n"
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasNoArgConstructor(Class t) {
|
||||
exists(Constructor c | c.getDeclaringType() = t |
|
||||
c.getNumberOfParameters() = 0 and
|
||||
not c.isPrivate()
|
||||
)
|
||||
}
|
||||
|
||||
private RefType getAReferencedType(RefType t) {
|
||||
result = t.(GeneratedType).getAGeneratedType()
|
||||
or
|
||||
result =
|
||||
getAReferencedType(any(NestedType nt |
|
||||
nt.getEnclosingType().getSourceDeclaration() = t.getSourceDeclaration()
|
||||
))
|
||||
or
|
||||
exists(RefType t1 | t1 = getAReferencedType(t) |
|
||||
result = t1.(NestedType).getEnclosingType()
|
||||
or
|
||||
result = t1.getSourceDeclaration()
|
||||
or
|
||||
result = t1.(ParameterizedType).getATypeArgument()
|
||||
or
|
||||
result = t1.(Array).getComponentType()
|
||||
or
|
||||
result = t1.(BoundedType).getATypeBound().getType()
|
||||
)
|
||||
}
|
||||
|
||||
/** A top level type whose file should be stubbed */
|
||||
class GeneratedTopLevel extends TopLevelType {
|
||||
GeneratedTopLevel() {
|
||||
this = this.getSourceDeclaration() and
|
||||
this instanceof GeneratedType
|
||||
}
|
||||
|
||||
private TopLevelType getAnImportedType() {
|
||||
result = getAReferencedType(this).getSourceDeclaration()
|
||||
}
|
||||
|
||||
private string stubAnImport() {
|
||||
exists(RefType t, string pkg, string name |
|
||||
t = getAnImportedType() and
|
||||
(t instanceof Class or t instanceof Interface) and
|
||||
t.hasQualifiedName(pkg, name) and
|
||||
t != this and
|
||||
pkg != "java.lang"
|
||||
|
|
||||
result = "import " + pkg + "." + name + ";\n"
|
||||
)
|
||||
}
|
||||
|
||||
private string stubImports() { result = concat(stubAnImport()) + "\n" }
|
||||
|
||||
private string stubPackage() {
|
||||
if this.getPackage().getName() != ""
|
||||
then result = "package " + this.getPackage().getName() + ";\n\n"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string stubComment() {
|
||||
result =
|
||||
"// Generated automatically from " + this.getQualifiedName() + " for testing purposes\n\n"
|
||||
}
|
||||
|
||||
/** Creates a full stub for the file containing this type. */
|
||||
string stubFile() {
|
||||
result = stubComment() + stubPackage() + stubImports() + this.(GeneratedType).getStub() + "\n"
|
||||
}
|
||||
}
|
||||
178
java/ql/src/utils/makeStubs.py
Executable file
178
java/ql/src/utils/makeStubs.py
Executable file
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Tool to generate Java stubs for qltests
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import json
|
||||
import glob
|
||||
import shlex
|
||||
import shutil
|
||||
import tempfile
|
||||
from shutil import copyfile
|
||||
|
||||
|
||||
def print_usage(exit_code=1):
|
||||
print("Usage: makeStubs.py testDir stubDir [pom.xml]\n",
|
||||
"testDir: the directory containing the qltest to be stubbed.\n"
|
||||
" Should contain an 'options' file pointing to 'stubDir'.\n"
|
||||
" This file should be in the same format as a normal 'options' file.\n",
|
||||
"stubDir: the directory to output the generated stubs to\n",
|
||||
"pom.xml: a 'pom.xml' file that can be used to build the project\n",
|
||||
" If the test can be extracted without a 'pom.xml', this argument can be ommitted.")
|
||||
exit(exit_code)
|
||||
|
||||
|
||||
if "--help" in sys.argv or "-h" in sys.argv:
|
||||
print_usage(0)
|
||||
|
||||
if len(sys.argv) not in [3, 4]:
|
||||
print_usage()
|
||||
|
||||
testDir = os.path.normpath(sys.argv[1])
|
||||
stubDir = os.path.normpath(sys.argv[2])
|
||||
|
||||
|
||||
def check_dir_exists(path):
|
||||
if not os.path.isdir(path):
|
||||
print(path, "does not exist or is not a directory")
|
||||
exit(1)
|
||||
|
||||
|
||||
def check_file_exists(path):
|
||||
if not os.path.isfile(path):
|
||||
print(path, "does not exist or is not a regular file")
|
||||
exit(1)
|
||||
|
||||
|
||||
check_dir_exists(testDir)
|
||||
check_dir_exists(stubDir)
|
||||
|
||||
optionsFile = os.path.join(testDir, "options")
|
||||
|
||||
check_file_exists(optionsFile)
|
||||
|
||||
# Does it contain a .ql file and a .java file?
|
||||
|
||||
foundJava = any(f.endswith(".java") for f in os.listdir(testDir))
|
||||
foundQL = any(f.endswith(".ql") or f.endswith(".qlref")
|
||||
for f in os.listdir(testDir))
|
||||
|
||||
if not foundQL:
|
||||
print("Test directory does not contain .ql files. Please specify a working qltest directory.")
|
||||
exit(1)
|
||||
|
||||
if not foundJava:
|
||||
print("Test directory does not contain .java files. Please specify a working qltest directory.")
|
||||
exit(1)
|
||||
|
||||
|
||||
workDir = tempfile.TemporaryDirectory().name
|
||||
|
||||
print("Created temporary directory '%s'" % workDir)
|
||||
|
||||
javaQueries = os.path.abspath(os.path.dirname(sys.argv[0]))
|
||||
outputBqrsFile = os.path.join(workDir, 'output.bqrs')
|
||||
outputJsonFile = os.path.join(workDir, 'output.json')
|
||||
|
||||
# Make a database that touches all types whose methods we want to test:
|
||||
print("Creating Maven project")
|
||||
projectDir = os.path.join(workDir, "mavenProject")
|
||||
|
||||
if len(sys.argv) == 4:
|
||||
projectTestPkgDir = os.path.join(projectDir, "src", "main", "java", "test")
|
||||
shutil.copytree(testDir, projectTestPkgDir,
|
||||
ignore=shutil.ignore_patterns('*.testproj'))
|
||||
|
||||
try:
|
||||
shutil.copyfile(sys.argv[3], os.path.join(projectDir, "pom.xml"))
|
||||
except Exception as e:
|
||||
print("Failed to read project POM %s: %s" %
|
||||
(sys.argv[2], e), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
# if `pom.xml` is omitted, simply copy the test directory to `projectDir`
|
||||
shutil.copytree(testDir, projectDir,
|
||||
ignore=shutil.ignore_patterns('*.testproj'))
|
||||
|
||||
dbDir = os.path.join(workDir, "db")
|
||||
|
||||
|
||||
def print_javac_output():
|
||||
logFiles = glob.glob(os.path.join(dbDir, "log", "javac-output*"))
|
||||
|
||||
if not(logFiles):
|
||||
print("\nNo javac output found.")
|
||||
else:
|
||||
logFile = logFiles[0]
|
||||
print("\nJavac output:\n")
|
||||
|
||||
with open(logFile) as f:
|
||||
for line in f:
|
||||
b1 = line.find(']')
|
||||
b2 = line.find(']', b1+1)
|
||||
print(line[b2+2:], end="")
|
||||
|
||||
|
||||
def run(cmd):
|
||||
"""Runs the given command, returning the exit code (nonzero on failure)"""
|
||||
print('\nRunning: ' + shlex.join(cmd) + '\n')
|
||||
return subprocess.call(cmd)
|
||||
|
||||
|
||||
print("Stubbing qltest in", testDir)
|
||||
|
||||
if run(['codeql', 'database', 'create', '--language=java', '--source-root='+projectDir, dbDir]):
|
||||
print_javac_output()
|
||||
print("codeql database create failed. Please fix up the test before proceeding.")
|
||||
exit(1)
|
||||
|
||||
if not os.path.isdir(dbDir):
|
||||
print("Expected database directory " + dbDir + " not found.")
|
||||
exit(1)
|
||||
|
||||
if run(['codeql', 'query', 'run', os.path.join(javaQueries, 'MinimalStubsFromSource.ql'), '--database', dbDir, '--output', outputBqrsFile]):
|
||||
print('Failed to run the query to generate the stubs.')
|
||||
exit(1)
|
||||
|
||||
if run(['codeql', 'bqrs', 'decode', outputBqrsFile, '--format=json', '--output', outputJsonFile]):
|
||||
print('Failed to convert ' + outputBqrsFile + ' to JSON.')
|
||||
exit(1)
|
||||
|
||||
with open(outputJsonFile) as f:
|
||||
results = json.load(f)
|
||||
|
||||
try:
|
||||
results['#select']['tuples']
|
||||
results['noGeneratedStubs']['tuples']
|
||||
results['multipleGeneratedStubs']['tuples']
|
||||
except KeyError:
|
||||
print('Unexpected JSON output - no tuples found')
|
||||
exit(1)
|
||||
|
||||
for (typ,) in results['noGeneratedStubs']['tuples']:
|
||||
print(f"WARNING: No stubs generated for {typ}. This is probably a bug.")
|
||||
|
||||
for (typ,) in results['multipleGeneratedStubs']['tuples']:
|
||||
print(
|
||||
f"WARNING: Multiple stubs generated for {typ}. This is probably a bug. One will be chosen arbitrarily.")
|
||||
|
||||
for (typ, stub) in results['#select']['tuples']:
|
||||
stubFile = os.path.join(stubDir, *typ.split(".")) + ".java"
|
||||
os.makedirs(os.path.dirname(stubFile), exist_ok=True)
|
||||
with open(stubFile, "w") as f:
|
||||
f.write(stub)
|
||||
|
||||
print("Verifying stub correctness")
|
||||
|
||||
if run(['codeql', 'test', 'run', testDir]):
|
||||
print_javac_output()
|
||||
print('\nTest failed. You may need to fix up the generated stubs.')
|
||||
exit(1)
|
||||
|
||||
os.rmdir(workDir)
|
||||
|
||||
print("\nStub generation successful!")
|
||||
|
||||
exit(0)
|
||||
Reference in New Issue
Block a user