// Souffle - A Datalog Compiler
// Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved
// Licensed under the Universal Permissive License v 1.0 as shown at:
// - https://opensource.org/licenses/UPL
// - <souffle root>/licenses/SOUFFLE-UPL.txt

///////////
// Types //
///////////

.type Type <: symbol
.type VarRef <: symbol
.type InvocationRef <: symbol
.type HeapAllocationRef <: symbol
.type FieldSignatureRef <: symbol
.type MethodSignatureRef <: symbol
.type MethodDescriptorRef <: symbol
.type Int <: symbol


/////////
// EDB //
/////////

.decl DirectFieldIndexSignature(class:Type, index:FieldSignatureRef, field:FieldSignatureRef)
.input DirectFieldIndexSignature()
.decl DirectSuperclass(subclass:Type, superclass:Type)
.input DirectSuperclass()
.decl DirectSuperinterface(type:Type, superinterface:Type)
.input DirectSuperinterface()
.decl HeapAllocationType(heapAllocation:HeapAllocationRef, allocationType:Type)
.input HeapAllocationType()
.decl ThisVar(method:MethodSignatureRef, thisVariable:VarRef)
.input ThisVar()
.decl ReturnVar(returnVariable:VarRef, method:MethodSignatureRef)
.input ReturnVar()
.decl SpecialMethodInvocationSignature(callsite:InvocationRef, calledMethod:MethodSignatureRef)
.input SpecialMethodInvocationSignature()
.decl SpecialMethodInvocationBase(callsite:InvocationRef, instanceVariable:VarRef)
.input SpecialMethodInvocationBase()
.decl StaticMethodInvocationSignature(callsite:InvocationRef, calledMethod:MethodSignatureRef)
.input StaticMethodInvocationSignature()
.decl VirtualMethodInvocationSignature(callsite:InvocationRef, calledMethod:MethodSignatureRef)
.input VirtualMethodInvocationSignature()
.decl VirtualMethodInvocationBase(callsite:InvocationRef, instanceVariable:VarRef)
.input VirtualMethodInvocationBase()
.decl VarType(variable:VarRef, type:Type)
.input VarType()
.decl FormalParam(index:Int, containingMethod:MethodSignatureRef, variable:VarRef)
.input FormalParam()
.decl ActualParam(index:Int, callsite:InvocationRef, actualVariable:VarRef)
.input ActualParam()
.decl AssignReturnValue(callsite:InvocationRef, variableAssignedTo:VarRef)
.input AssignReturnValue()
.decl AssignCast(castType:Type, variableCast:VarRef, variableAssignedTp:VarRef, containingMethod:MethodSignatureRef)
.input AssignCast()
.decl AssignLocal(variableAssigned:VarRef, variableAssignedTo:VarRef, containingMethod:MethodSignatureRef)
.input AssignLocal()
.decl AssignHeapAllocation(heapAllocation:HeapAllocationRef, variableAssignedTo:VarRef, containingMethod:MethodSignatureRef)
.input AssignHeapAllocation()
.decl LoadInstanceFieldIndex(instanceVariable:VarRef, field:FieldSignatureRef, variableAssignedTo:VarRef, containingMethod:MethodSignatureRef)
.input LoadInstanceFieldIndex()
.decl StoreInstanceFieldIndex(storedVariable:VarRef, instanceVariable:VarRef, field:FieldSignatureRef, containingMethod:MethodSignatureRef)
.input StoreInstanceFieldIndex()


.decl InstanceVar(instanceVariable:VarRef)
InstanceVar(var) :- LoadInstanceFieldIndex(var, _, _, _).
InstanceVar(var) :- StoreInstanceFieldIndex(_, var, _, _).

.decl Superclass(subclass:Type, superclass:Type)
Superclass(sub, super) :- DirectSuperclass(sub, super).
Superclass(sub, super) :- Superclass(sub, x), DirectSuperclass(x, super).

.decl Supertype(subtype:Type, supertype:Type)
Supertype(sub, super) :- Superclass(sub, super).
Supertype(type, interface) :- DirectSuperinterface(type, interface).

.decl Invocation(callsite:InvocationRef, calledMethod:MethodSignatureRef)
Invocation(callsite, calledMethod) :- SpecialMethodInvocationSignature(callsite, calledMethod).
Invocation(callsite, calledMethod) :- StaticMethodInvocationSignature(callsite, calledMethod).
Invocation(callsite, calledMethod) :- VirtualMethodInvocationSignature(callsite, calledMethod).

.decl InvocationBase(callsite:InvocationRef, instanceVariable:VarRef)
InvocationBase(callsite, var) :- VirtualMethodInvocationBase(callsite, var).
InvocationBase(callsite, var) :- SpecialMethodInvocationBase(callsite, var).

.decl InstanceVarType(variable:VarRef, type:Type)
InstanceVarType(var, type) :-
  VarType(var, type),
  InstanceVar(var).

.decl InstanceTypes(type:Type)
InstanceTypes(x) :-
  Supertype(x, _),
  InstanceVarType(_, x).

InstanceTypes(x) :-
  Supertype(_, x),
  InstanceVarType(_, x).

.decl IsInstanceTypeCompatible(subclass:Type, superclass:Type)
IsInstanceTypeCompatible(sub, super) :-
  Supertype(sub, super).

IsInstanceTypeCompatible(t, t) :-
  InstanceTypes(t).

.decl InstanceTypeComparable(subclass:Type, superclass:Type)
InstanceTypeComparable(type1, type2) :-
  IsInstanceTypeCompatible(type1, type2).

InstanceTypeComparable(type1, type2) :-
  IsInstanceTypeCompatible(type2, type1).

.decl ComparableInstanceVar(variable:VarRef, variable2:VarRef)
ComparableInstanceVar(var1, var2) :-
 InstanceVarType(var1, type1),
 InstanceVarType(var2, type2),
 InstanceTypeComparable(type1, type2).

.decl Assign(varAssigned:VarRef, varAssignedTo:VarRef)
Assign(localVarAssigned, localVarAssignedTo) :- AssignLocal(localVarAssigned, localVarAssignedTo, _).
Assign(actualParam, formalParam) :- Invocation(callsite, method), FormalParam(index, method, formalParam), ActualParam(index, callsite, actualParam).
Assign(returnVar, localVar) :- ReturnVar(returnVar, method), Invocation(callsite, method), AssignReturnValue(callsite, localVar).
Assign(base, this) :- InvocationBase(callsite, base), Invocation(callsite, method), ThisVar(method, this).

.decl VarPointsTo(variable:VarRef, heapAllocation:HeapAllocationRef)
.output VarPointsTo()
VarPointsTo(var, heap)  :-
  AssignHeapAllocation(heap, var, _).

VarPointsTo(var1, heap) :-
  Assign(var2, var1),
  VarPointsTo(var2, heap).

VarPointsTo(var1, heap) :-
  AssignCast(type, var2, var1, _),
  VarPointsTo(var2, heap),
  HeapAllocationType(heap, type).

VarPointsTo(var1, heap) :-
  AssignCast(varType, var2, var1, _),
  VarPointsTo(var2, heap),
  HeapAllocationType(heap, heapType),
  Supertype(heapType, varType).

.decl InstanceVarPointsTo(variable:VarRef, heapAllocation:HeapAllocationRef)
InstanceVarPointsTo(var1, var2) :-
  InstanceVar(var1),
  VarPointsTo(var1, var2).

.decl LoadInstanceVarPointsTo(variable:VarRef, heapAllocation:HeapAllocationRef)
LoadInstanceVarPointsTo(var, heap)  :-
  InstanceVarPointsTo(var, heap),
  LoadInstanceFieldIndex(var, _, _, _).

.decl StoreInstanceVarPointsTo(variable:VarRef, heapAllocation:HeapAllocationRef)
StoreInstanceVarPointsTo(var, heap) :-
  InstanceVarPointsTo(var, heap),
  StoreInstanceFieldIndex(_, var, _, _).

.decl Alias(v1:VarRef, v2:VarRef)
Alias(v1, v2) :-
  LoadInstanceVarPointsTo(v1, h),
  StoreInstanceVarPointsTo(v2, h),
  InstanceVarType(v1, t1),
  InstanceVarType(v2, t2),
  InstanceTypeComparable(t1, t2).

Assign(storedVar, varAssignedTo) :-
  Alias(iLoadVar, iStoreVar),
  StoreInstanceFieldIndex(storedVar, iStoreVar, field, _),
  LoadInstanceFieldIndex(iLoadVar, field, varAssignedTo, _).

