Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat-fix(pointer-analysis): expand configuration to enforce disable #1229

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/dataflow/environments/resolve-by-name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ const TargetTypePredicate = {
* @param environment - The current environment used for name resolution
* @param target - The target (meta) type of the identifier to resolve
*
* @returns A list of possible definitions of the identifier (one if the definition location is exactly and always known), or `undefined` if the identifier is undefined in the current scope/with the current environment information.
* @returns A list of possible identifier definitions (one if the definition location is exactly and always known), or `undefined`
* if the identifier is undefined in the current scope/with the current environment information.
*/
export function resolveByName(name: Identifier, environment: REnvironmentInformation, target: ReferenceType = ReferenceType.Unknown): IdentifierDefinition[] | undefined {
let current: IEnvironment = environment.current;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { isParentContainerIndex } from '../../../../../graph/vertex';
import type { RArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-argument';
import { RoleInParent } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/role';
import { filterIndices, resolveSingleIndex } from '../../../../../../util/list-access';
import { getConfig } from '../../../../../../config';

interface TableAssignmentProcessorMarker {
definitionRootNodes: NodeId[]
Expand Down Expand Up @@ -200,35 +201,37 @@ function processStringBasedAccess<OtherInfo>(
return fnCall;
}

let accessedIndicesCollection: ContainerIndicesCollection;
// If the accessedArg is a symbol, it's either a simple access or the base case of a nested access
if(accessedArg.value?.type === RType.Symbol) {
accessedIndicesCollection = resolveSingleIndex(accessedArg, accessArg, data.environment);
} else {
// Higher access call
const underlyingAccessId = accessedArg.value?.info.id ?? -1;
const vertex = fnCall.information.graph.getVertex(underlyingAccessId);
const subIndices = vertex?.indicesCollection
?.flatMap(indices => indices.indices)
?.flatMap(index => (index as ContainerParentIndex)?.subIndices ?? []);
if(subIndices) {
accessedIndicesCollection = filterIndices(subIndices, accessArg);
if(getConfig().solver.pointerTracking) {
let accessedIndicesCollection: ContainerIndicesCollection;
// If the accessedArg is a symbol, it's either a simple access or the base case of a nested access
if(accessedArg.value?.type === RType.Symbol) {
accessedIndicesCollection = resolveSingleIndex(accessedArg, accessArg, data.environment);
} else {
// Higher access call
const underlyingAccessId = accessedArg.value?.info.id ?? -1;
const vertex = fnCall.information.graph.getVertex(underlyingAccessId);
const subIndices = vertex?.indicesCollection
?.flatMap(indices => indices.indices)
?.flatMap(index => (index as ContainerParentIndex)?.subIndices ?? []);
if(subIndices) {
accessedIndicesCollection = filterIndices(subIndices, accessArg);
}
}
}

// Add indices to vertex afterward
if(accessedIndicesCollection) {
const vertex = fnCall.information.graph.getVertex(rootId);
if(vertex) {
vertex.indicesCollection = accessedIndicesCollection;
}
// Add indices to vertex afterward
if(accessedIndicesCollection) {
const vertex = fnCall.information.graph.getVertex(rootId);
if(vertex) {
vertex.indicesCollection = accessedIndicesCollection;
}

// When access has no access as parent, it's the top most
const rootNode = data.completeAst.idMap.get(rootId);
const parentNode = data.completeAst.idMap.get(rootNode?.info.parent ?? -1);
if(parentNode?.type !== RType.Access) {
// Only reference indices in top most access
referenceIndices(accessedIndicesCollection, fnCall, name.info.id);
// When access has no access as parent, it's the top most
const rootNode = data.completeAst.idMap.get(rootId);
const parentNode = data.completeAst.idMap.get(rootNode?.info.parent ?? -1);
if(parentNode?.type !== RType.Access) {
// Only reference indices in top most access
referenceIndices(accessedIndicesCollection, fnCall, name.info.id);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import type { REnvironmentInformation } from '../../../../../environments/enviro
import type { DataflowGraph } from '../../../../../graph/graph';
import { getAliases } from '../../../../../environments/resolve-by-name';
import { addSubIndicesToLeafIndices } from '../../../../../../util/list-access';
import { getConfig } from '../../../../../../config';

function toReplacementSymbol<OtherInfo>(target: RNodeWithParent<OtherInfo & ParentInformation> & Base<OtherInfo> & Location, prefix: string, superAssignment: boolean): RSymbol<OtherInfo & ParentInformation> {
return {
Expand Down Expand Up @@ -251,8 +252,7 @@ export interface AssignmentToSymbolParameters<OtherInfo> extends AssignmentConfi
* @param nodeToDefine - `x`
* @param sourceIds - `v`
* @param rootIdOfAssignment - `<-`
* @param quoteSource - whether to quote the source (i.e., define `x` without a direct reference to `v`)
* @param superAssignment - whether this is a super assignment (i.e., `<<-`)
* @param config - configuration for the assignment processing
*/
export function markAsAssignment(
information: {
Expand All @@ -264,24 +264,26 @@ export function markAsAssignment(
rootIdOfAssignment: NodeId,
config?: AssignmentConfiguration | undefined,
) {
let indicesCollection: ContainerIndicesCollection = undefined;
if(sourceIds.length === 1) {
// support for tracking indices
// Indices were defined for the vertex e.g. a <- list(c = 1) or a$b <- list(c = 1)
indicesCollection = information.graph.getVertex(sourceIds[0])?.indicesCollection;
}
// Indices defined by replacement operation e.g. $<-
if(config?.indicesCollection !== undefined) {
// If there were indices stored in the vertex, then a container was defined
// and assigned to the index of another container e.g. a$b <- list(c = 1)
if(indicesCollection) {
indicesCollection = addSubIndicesToLeafIndices(config.indicesCollection, indicesCollection);
} else {
// No indices were defined for the vertex e.g. a$b <- 2
indicesCollection = config.indicesCollection;
if(getConfig().solver.pointerTracking) {
let indicesCollection: ContainerIndicesCollection = undefined;
if(sourceIds.length === 1) {
// support for tracking indices
// Indices were defined for the vertex e.g. a <- list(c = 1) or a$b <- list(c = 1)
indicesCollection = information.graph.getVertex(sourceIds[0])?.indicesCollection;
}
// Indices defined by replacement operation e.g. $<-
if(config?.indicesCollection !== undefined) {
// If there were indices stored in the vertex, then a container was defined
// and assigned to the index of another container e.g. a$b <- list(c = 1)
if(indicesCollection) {
indicesCollection = addSubIndicesToLeafIndices(config.indicesCollection, indicesCollection);
} else {
// No indices were defined for the vertex e.g. a$b <- 2
indicesCollection = config.indicesCollection;
}
}
nodeToDefine.indicesCollection ??= indicesCollection;
}
nodeToDefine.indicesCollection ??= indicesCollection;

information.environment = define(nodeToDefine, config?.superAssignment, information.environment);
information.graph.setDefinitionOfVertex(nodeToDefine);
Expand All @@ -291,12 +293,14 @@ export function markAsAssignment(
}
}
information.graph.addEdge(nodeToDefine, rootIdOfAssignment, EdgeType.DefinedBy);
// kinda dirty, but we have to remove existing read edges for the symbol, added by the child
const out = information.graph.outgoingEdges(nodeToDefine.nodeId);
for(const [id,edge] of (out?? [])) {
edge.types &= ~EdgeType.Reads;
if(edge.types == 0) {
out?.delete(id);
if(getConfig().solver.pointerTracking) {
// kinda dirty, but we have to remove existing read edges for the symbol, added by the child
const out = information.graph.outgoingEdges(nodeToDefine.nodeId);
for(const [id, edge] of (out ?? [])) {
edge.types &= ~EdgeType.Reads;
if(edge.types == 0) {
out?.delete(id);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { graphToMermaidUrl } from '../../../../../../util/mermaid/dfg';
import { RoleInParent } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/role';
import { RType } from '../../../../../../r-bridge/lang-4.x/ast/model/type';
import { constructNestedAccess } from '../../../../../../util/list-access';
import { getConfig } from '../../../../../../config';

export function processReplacementFunction<OtherInfo>(
name: RSymbol<OtherInfo & ParentInformation>,
Expand All @@ -38,7 +39,7 @@ export function processReplacementFunction<OtherInfo>(
expensiveTrace(dataflowLogger, () => `Replacement ${name.content} with ${JSON.stringify(args)}, processing`);

let indices: ContainerIndicesCollection = undefined;
if(name.content === '$<-') {
if(name.content === '$<-' && getConfig().solver.pointerTracking) {
const nonEmptyArgs = args.filter(arg => arg !== EmptyArgument);
const accessedArg = nonEmptyArgs.find(arg => arg.info.role === RoleInParent.Accessed);
const accessArg = nonEmptyArgs.find(arg => arg.info.role === RoleInParent.IndexAccess);
Expand Down
19 changes: 19 additions & 0 deletions test/functionality/_helper/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { FlowrConfigOptions } from '../../../src/config';
import { setConfig , getConfig } from '../../../src/config';
import { afterAll, beforeAll } from 'vitest';
import type { DeepPartial } from 'ts-essentials';
import { deepMergeObject } from '../../../src/util/objects';


/**
* Temporarily sets the config to the given value for all tests in the suite.
*/
export function useConfigForTest(config: DeepPartial<FlowrConfigOptions>): void {
const currentConfig = getConfig();
beforeAll(() => {
setConfig(deepMergeObject(currentConfig, config) as FlowrConfigOptions);
});
afterAll(() => {
setConfig(currentConfig);
});
}
19 changes: 17 additions & 2 deletions test/functionality/slicing/pointer-analysis/list-access.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { describe, it } from 'vitest';
import { assertSliced, withShell } from '../../_helper/shell';
import { label } from '../../_helper/label';
import { useConfigForTest } from '../../_helper/config';

describe.sequential('List access', withShell(shell => {
const basicCapabilities = ['name-normal', 'function-calls', 'named-arguments', 'dollar-access', 'subsetting'] as const;
useConfigForTest({ solver: { pointerTracking: true } });
describe('Simple access', () => {
assertSliced(label('List with single element', basicCapabilities), shell,
'person <- list(name = "John")\nprint(person$name)',
Expand Down Expand Up @@ -123,7 +125,7 @@ result <- person$age`,
`person <- list(age = 24, name = "John", is_male = TRUE)
result <- person$age`,
);

describe('Access within conditionals', () => {
assertSliced(label('Only a potential overwrite', basicCapabilities),
shell,
Expand Down Expand Up @@ -175,7 +177,7 @@ print(wrapper$person$age)`);
});
});
});

describe('Nested lists', () => {
assertSliced(label('When index of nested list is overwritten, then overwrite is also in slice', basicCapabilities),
shell,
Expand Down Expand Up @@ -382,4 +384,17 @@ result <- person$name`,
);
});
});

describe('Config flag', () => {
useConfigForTest({ solver: { pointerTracking: false } });
assertSliced(label('When flag is false, then list access is not in slice', ['call-normal']), shell,
`person <- list(age = 24, name = "John")
person$name <- "Jane"
person$age <- 23
print(person$name)`, ['4@print'], `person <- list(age = 24, name = "John")
person$name <- "Jane"
person$age <- 23
print(person$name)`
);
});
}));
Loading