Created
March 9, 2017 11:16
-
-
Save jurgenvinju/60645058b6d0b0ccce0fc6f856da6ea6 to your computer and use it in GitHub Desktop.
A "fixed" JavaToObjectFlow algorithm which does not ignore the references to values of primitive types. For maps, this version also lets the key flow into the receiver.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@doc{ | |
Synopsis: mapping all of Java to the object flow language | |
Description: | |
More information can be found in [ObjectFlow]. | |
} | |
module lang::java::flow::JavaToObjectFlow2 | |
import IO; | |
import Set; | |
import List; | |
import analysis::flow::ObjectFlow; | |
import lang::java::m3::TypeSymbol; | |
import lang::java::jdt::m3::AST; | |
FlowProgram createOFG(loc project) = createOFG(createAstsFromEclipseProject(project, true)); | |
FlowProgram createOFG(set[Declaration] asts) { | |
println("Getting decls"); | |
decls = getDeclarations(asts); | |
println("Getting stms"); | |
stms = getStatements(asts); | |
return flowProgram(decls, stms); | |
} | |
set[str] containerClasses = { | |
"/java/util/Map" | |
,"/java/util/HashMap" | |
,"/java/util/Collection" | |
,"/java/util/Set" | |
,"/java/util/HashSet" | |
,"/java/util/LinkedHashSet" | |
,"/java/util/List" | |
,"/java/util/ArrayList" | |
,"/java/util/LinkedList" | |
}; | |
map[str, int] insertArgs = ( | |
"insert": 0 | |
, "insertAll": 0 | |
, "put": 1 | |
, "putAll": 0 | |
, "add": 0 | |
, "addAll": 0 | |
); | |
map[str,int] keyArgs = insertArgs + ("put":0); | |
Expression correctInsertArg(Expression recv, str name, list[Expression] args) { | |
return args[insertArgs[name]]; | |
} | |
Expression correctKeyArg(Expression recv, str name, list[Expression] args) { | |
return args[keyArgs[name]]; | |
} | |
bool isContainerInsert(Expression recv, str name) { | |
if (recv@typ has decl) { | |
tp = (recv@typ).decl.path; | |
if (tp in containerClasses) { | |
return name in insertArgs; | |
} | |
} | |
return false; | |
} | |
bool isContainerExtract(Expression recv, str name) { | |
tp = ((recv@typ).decl?|unknown:///|).path; | |
if (tp in containerClasses) { | |
switch (name) { | |
case "get": return true; | |
case "iterator": return true; | |
case "toArray": return true; | |
case "entrySet": return true; | |
case "values": return true; | |
} | |
} | |
return false; | |
} | |
data Expression = more(set[Expression] alts); | |
list[Declaration] fixCollections(list[Declaration] ast) { | |
return visit (ast) { | |
case oe:methodCall(_, Expression receiver, methodName,args): { | |
if (isContainerInsert(receiver, methodName)) { | |
insert more({assignment(receiver, "=", correctInsertArg(receiver, methodName, args)) | |
[@typ = receiver@typ] | |
[@src = oe@src], | |
assignment(receiver, "=", correctKeyArg(receiver, methodName, args)) // this produces only for map put a different answer, the set removes the duplicates | |
[@typ = receiver@typ] | |
[@src = oe@src] | |
}); | |
} | |
else if(isContainerExtract(receiver, methodName)) { | |
insert receiver; | |
} | |
} | |
}; | |
} | |
set[str] primitiveTypes = { | |
"Byte", "java.lang.Byte" | |
, "Character", "java.lang.Character" | |
, "Short", "java.lang.Short" | |
, "Integer", "java.lang.Integer" | |
, "Long", "java.lang.Long" | |
, "Float", "java.lang.Float" | |
, "Double", "java.lang.Double" | |
}; | |
set[FlowDecl] getDeclarations(set[Declaration] asts) | |
= { FlowDecl::attribute(v@decl) | /field(t,frags) <- asts, v <- frags} | |
+ { FlowDecl::method(m@decl, [p@decl | p:parameter(t,_,_) <- params]) | /m:Declaration::method(_,_, list[Declaration] params, _, _) <- asts} | |
+ { FlowDecl::method(m@decl, [p@decl | p:parameter(t,_,_) <- params]) | /m:Declaration::method(_,_, list[Declaration] params, _) <- asts} | |
+ { FlowDecl::constructor(c@decl, [p@decl | p:parameter(t,_,_) <- params]) | /c:Declaration::constructor(_, list[Declaration] params, _,_) <- asts} | |
// add implicit constructor | |
+ { FlowDecl::constructor((c@decl)[scheme="java+constructor"] + "<name>()", []) | /c:class(name, _, _, b) <- asts, !(Declaration::constructor(_, _, _, _) <- b)} | |
; | |
loc lhsDecl(arrayAccess(e,_)) = e@decl; | |
loc lhsDecl(f:fieldAccess(_,_,_)) = f@decl; | |
loc lhsDecl(f:fieldAccess(_,_)) = f@decl; | |
loc lhsDecl(v:variable(_,_)) = v@decl; | |
loc lhsDecl(s:simpleName(_)) = s@decl; | |
loc lhsDecl(q:qualifiedName(_,_)) = q@decl; | |
default loc lhsDecl(Expression e) { throw "forgot: <e>"; } | |
set[FlowStm] getStatements(set[Declaration] asts) { | |
allMethods | |
= [ m | /m:Declaration::method(_,_,_,_,_) <- asts] | |
+ [Declaration::method(t, n, p, e, empty())[@decl=m@decl] | /m:Declaration::method(Type t,n,p,e) <- asts] | |
+ [Declaration::method(simpleType(simpleName(n)), n, p, e, b)[@decl=m@decl] | /m:Declaration::constructor(str n,p,e, b) <- asts] | |
; | |
allMethods = fixCollections(allMethods); | |
// now remove all nested classes to make all statements relative to a method | |
allMethods = visit(allMethods) { | |
case declarationExpression(Declaration::class(_)) => Expression::null() | |
case declarationExpression(Declaration::class(_,_,_,_)) => Expression::null() | |
case declarationExpression(Declaration::enum(_,_,_,_)) => Expression::null() | |
case declarationStatement(Declaration::class(_)) => empty() | |
case declarationStatement(Declaration::class(_,_,_,_)) => empty() | |
case declarationStatement(Declaration::enum(_,_,_,_)) => empty() | |
}; | |
set[FlowStm] result = {}; | |
for (m:Declaration::method(_, _, _, _, b) <- allMethods) { | |
top-down-break visit(b) { | |
case \return(e) : | |
result += { *translate(m@decl, m@decl + "return", e)}; | |
case e:Expression::assignment(l,_,r) : | |
result += { *translate(m@decl, lhsDecl(l), r)}; | |
case v:Expression::variable(_,_,r) : | |
result += { *translate(m@decl, v@decl, r)}; | |
// regular method calls with no target | |
case m2:Expression::methodCall(_ ,_, _): | |
result += { *translate(m@decl, emptyId, m2)}; | |
case m2:Expression::methodCall(_ ,_, _, _): | |
result += { *translate(m@decl, emptyId, m2)}; | |
} | |
} | |
return result; | |
} | |
// TODO: handle a.b.c => B.c | |
set[FlowStm] translate(loc base, loc target, c:cast(_, e)) { | |
result = translate(base, target, e); | |
return { s.target == target ? s[cast=c@typ.decl] : s | s <- result}; | |
} | |
set[FlowStm] translate(loc base, loc target, conditional(con, t, e)) | |
= translate(base, emptyId, con) | |
+ translate(base, target, t) | |
+ translate(base, target, e) | |
; | |
// TODO: check what the second argument could mean (Expr) | |
set[FlowStm] translate(loc base, loc target, f:fieldAccess(_,_,_)) | |
= {FlowStm::assign(target, emptyId, f@decl)}; | |
set[FlowStm] translate(loc base, loc target, f:fieldAccess(_,_)) | |
= {FlowStm::assign(target, emptyId, f@decl)}; | |
set[FlowStm] translate(loc base, loc target, s:simpleName(_)) | |
= {FlowStm::assign(target, emptyId, s@decl)}; | |
// nested assignment a = b = c; | |
set[FlowStm] translate(loc base, loc target, a:assignment(l,_,r)) | |
= translate(base, target, l) | |
+ translate(base, target, r) | |
; | |
set[FlowStm] translate(loc base, loc target, m:methodCall(s, n, a)) | |
= translate(base, target, methodCall(s, this(), n, a)[@decl=m@decl][@typ=m@typ][@src=m@src]); | |
set[FlowStm] translate(loc base, loc target, m:methodCall(_, r, n, a)) { | |
set[FlowStm] stms = {}; | |
loc recv = emptyId; | |
if (this() := r) { | |
recv = base+"this"; | |
} | |
else { | |
<newId, newStms> = unnestExpressions(base, [email protected], [r]); | |
if (size(newId) > 0) { | |
assert size(newId) == 1; | |
recv = getOneFrom(newId); | |
} | |
stms += newStms; | |
} | |
<args, newStms> = unnestExpressions(base, [email protected], a); | |
return newStms + { FlowStm::call(target, emptyId, recv, m@decl, args) }; | |
} | |
private Expression newObject(Type t, list[Expression] args, Expression original) { | |
assert original is newObject; | |
return newObject(t, args) | |
[@typ = original@typ] | |
[@src = original@src] | |
[@decl = original@decl]; | |
} | |
set[FlowStm] translate(loc base, loc target, ob:newObject(_, Type t, a)) | |
= translate(base, target, newObject(t, a, ob)); | |
set[FlowStm] translate(loc base, loc target, ob:newObject(_, Type t, a, _)) | |
= translate(base, target, newObject(t, a, ob)); | |
set[FlowStm] translate(loc base, loc target, ob:newObject(Type t, a,_)) | |
= translate(base, target, newObject(t, a, ob)); | |
set[FlowStm] translate(loc base, loc target, ob:newObject(Type t, a)) { | |
assert target != emptyId; | |
<args, stms> = unnestExpressions(base, [email protected], a); | |
return stms + { FlowStm::newAssign(target, [email protected], ob@decl, args)}; | |
} | |
bool simpleExpression(fieldAccess(_,_,_)) = true; | |
bool simpleExpression(fieldAccess(_,_)) = true; | |
bool simpleExpression(qualifiedName(_,e)) = simpleExpression(e); | |
bool simpleExpression(this()) = true; | |
bool simpleExpression(this(_)) = true; | |
bool simpleExpression(simpleName(_)) = true; | |
default bool simpleExpression(Expression e) = false; | |
Expression removeNesting(cast(_, e)) = removeNesting(e); | |
Expression removeNesting(arrayAccess(e, _)) = removeNesting(e); | |
Expression removeNesting(\bracket(e)) = removeNesting(e); | |
default Expression removeNesting(Expression e) = e; | |
// for arguments we have to unnestExpressions | |
// .. = new A(new B()); | |
// becomes | |
// __param<unique>_0 = new B(); | |
// .. = new A(__param<unique>_0); | |
tuple[list[loc], set[FlowStm]] unnestExpressions(loc prefix, int uniqNum, list[Expression] exprs) { | |
list[loc] ids = []; | |
set[FlowStm] newStms = {}; | |
for (i <- [0..size(exprs)], Expression ce := exprs[i]) { | |
ce = removeNesting(ce); | |
if (simpleExpression(ce)) { | |
if (ce is this) { | |
ids += [prefix + "this"]; | |
} | |
else { | |
ids += [ce@decl]; | |
} | |
} | |
else { | |
newId = prefix + "__param<uniqNum>_<i>"; | |
ids += [newId]; | |
newStms += translate(prefix, newId, ce); | |
} | |
} | |
return <ids, newStms>; | |
} | |
default set[FlowStm] translate(loc base, loc target, Expression e) = { *translate(base, target, ch) | Expression ch <- e}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment