Skip to content

Commit

Permalink
feat(r-graphics-pkg): rudiementary side effect support
Browse files Browse the repository at this point in the history
  • Loading branch information
EagleoutIce committed Oct 27, 2024
1 parent 1cf9166 commit 3847bf1
Show file tree
Hide file tree
Showing 22 changed files with 273 additions and 217 deletions.
17 changes: 7 additions & 10 deletions src/dataflow/environments/built-in.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@ import type { ForceArguments } from '../internal/process/functions/call/common';
import { processApply } from '../internal/process/functions/call/built-in/built-in-apply';
import { registerBuiltInDefinitions } from './built-in-config';
import { DefaultBuiltinConfig } from './default-builtin-config';
import {LinkTo} from "../../queries/catalog/call-context-query/call-context-query-format";
import {extractCFG} from "../../util/cfg/cfg";
import type { LinkTo } from '../../queries/catalog/call-context-query/call-context-query-format';



import {
identifyLinkToLastCallRelation
} from "../../queries/catalog/call-context-query/identify-link-to-last-call-relation";

export const BuiltIn = 'built-in';

Expand Down Expand Up @@ -64,11 +62,10 @@ export interface BuiltInIdentifierConstant<T = unknown> extends IdentifierRefere
value: T
}

/* TODO: improve support for link to */
export interface DefaultBuiltInProcessorConfiguration extends ForceArguments {
readonly returnsNthArgument?: number | 'last',
readonly cfg?: ExitPointType,
readonly readAllArguments?: boolean,
readonly returnsNthArgument?: number | 'last',
readonly cfg?: ExitPointType,
readonly readAllArguments?: boolean,
readonly hasUnknownSideEffects?: boolean | LinkTo<RegExp | string>
}

Expand Down Expand Up @@ -96,7 +93,7 @@ function defaultBuiltInProcessor<OtherInfo>(

if(config.hasUnknownSideEffects) {
if(typeof config.hasUnknownSideEffects !== 'boolean') {
res.graph.markIdForUnknownSideEffects(rootId, config.hasUnknownSideEffects)
res.graph.markIdForUnknownSideEffects(rootId, config.hasUnknownSideEffects);
} else {
res.graph.markIdForUnknownSideEffects(rootId);
}
Expand Down
8 changes: 4 additions & 4 deletions src/dataflow/environments/default-builtin-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ export const DefaultBuiltinConfig: BuiltInDefinitions = [
{ type: 'function', names: ['apply', 'tapply', 'Tapply'], processor: 'builtin:apply', config: { indexOfFunction: 2, nameOfFunctionArgument: 'FUN' }, assumePrimitive: false },
{ type: 'function', names: ['print', 'message', 'warning'], processor: 'builtin:default', config: { returnsNthArgument: 0, forceArgs: 'all', hasUnknownSideEffects: { type: 'link-to-last-call', callName: /^sink$/ } }, assumePrimitive: false },
// graphics base
{ type: 'function', names: ["plot", "map", "image", "boxplot", "barplot", "matplot", "hist", "stem", "density", "smoothScatter", "contour", "persp"],
processor: 'builtin:default', config: { forceArgs: 'all', hasUnknownSideEffects: { type: 'link-to-last-call', callName: /^pdf|jpeg|png|windows|postscript|xfig|bitmap|pictex|cairo_pdf|svg|bmp|tiff|X11|quartz$/ } }, assumePrimitive: true },
{ type: 'function', names: ['plot', 'map', 'image', 'boxplot', 'barplot', 'matplot', 'hist', 'stem', 'density', 'smoothScatter', 'contour', 'persp'],
processor: 'builtin:default', config: { forceArgs: 'all', hasUnknownSideEffects: { type: 'link-to-last-call', callName: /^pdf|jpeg|png|windows|postscript|xfig|bitmap|pictex|cairo_pdf|svg|bmp|tiff|X11|quartz$/ } }, assumePrimitive: true },
// graphics addons
{ type: 'function', names: ["points", "abline", "lines", "text", "legend", "title", "axis", "polygon", "polypath", "pie", "rect", "segments", "arrows", "symbols", "tiplabels"],
processor: 'builtin:default', config: { forceArgs: 'all', hasUnknownSideEffects: { type: 'link-to-last-call', callName: /^dev\.new|dev\.copy|plot|map|image|boxplot|barplot|matplot|hist|stem|density|smoothScatter|contour|persp$/ } }, assumePrimitive: true },
{ type: 'function', names: ['points', 'abline', 'lines', 'text', 'legend', 'title', 'axis', 'polygon', 'polypath', 'pie', 'rect', 'segments', 'arrows', 'symbols', 'tiplabels'],
processor: 'builtin:default', config: { forceArgs: 'all', hasUnknownSideEffects: { type: 'link-to-last-call', callName: /^dev\.new|dev\.copy|plot|map|image|boxplot|barplot|matplot|hist|stem|density|smoothScatter|contour|persp$/ } }, assumePrimitive: true },
{ type: 'function', names: ['('], processor: 'builtin:default', config: { returnsNthArgument: 0 }, assumePrimitive: true },
{ type: 'function', names: ['load', 'load_all', 'setwd', 'set.seed'], processor: 'builtin:default', config: { hasUnknownSideEffects: true, forceArgs: [true] }, assumePrimitive: false },
{ type: 'function', names: ['eval', 'body', 'formals', 'environment'], processor: 'builtin:default', config: { hasUnknownSideEffects: true, forceArgs: [true] }, assumePrimitive: false },
Expand Down
59 changes: 30 additions & 29 deletions src/dataflow/extractor.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import type {DataflowInformation} from './info';
import type {DataflowProcessors} from './processor';
import {processDataflowFor} from './processor';
import {processUninterestingLeaf} from './internal/process/process-uninteresting-leaf';
import {processSymbol} from './internal/process/process-symbol';
import {processFunctionCall} from './internal/process/functions/call/default-call-handling';
import {processFunctionParameter} from './internal/process/functions/process-parameter';
import {processFunctionArgument} from './internal/process/functions/process-argument';
import {processAsNamedCall} from './internal/process/process-named-call';
import {processValue} from './internal/process/process-value';
import {processNamedCall} from './internal/process/functions/call/named-call-handling';
import {wrapArgumentsUnnamed} from './internal/process/functions/call/argument/make-argument';
import {rangeFrom} from '../util/range';
import type {NormalizedAst, ParentInformation} from '../r-bridge/lang-4.x/ast/model/processing/decorate';
import {RType} from '../r-bridge/lang-4.x/ast/model/type';
import type {RParseRequest, RParseRequests} from '../r-bridge/retriever';
import {requestFingerprint} from '../r-bridge/retriever';
import {initializeCleanEnvironments} from './environments/environment';
import {standaloneSourceFile} from './internal/process/functions/call/built-in/built-in-source';
import {DataflowGraph} from "./graph/graph";
import {ControlFlowGraph, extractCFG} from "../util/cfg/cfg";
import {EdgeType} from "./graph/edge";
import type { DataflowInformation } from './info';
import type { DataflowProcessors } from './processor';
import { processDataflowFor } from './processor';
import { processUninterestingLeaf } from './internal/process/process-uninteresting-leaf';
import { processSymbol } from './internal/process/process-symbol';
import { processFunctionCall } from './internal/process/functions/call/default-call-handling';
import { processFunctionParameter } from './internal/process/functions/process-parameter';
import { processFunctionArgument } from './internal/process/functions/process-argument';
import { processAsNamedCall } from './internal/process/process-named-call';
import { processValue } from './internal/process/process-value';
import { processNamedCall } from './internal/process/functions/call/named-call-handling';
import { wrapArgumentsUnnamed } from './internal/process/functions/call/argument/make-argument';
import { rangeFrom } from '../util/range';
import type { NormalizedAst, ParentInformation } from '../r-bridge/lang-4.x/ast/model/processing/decorate';
import { RType } from '../r-bridge/lang-4.x/ast/model/type';
import type { RParseRequest, RParseRequests } from '../r-bridge/retriever';
import { requestFingerprint } from '../r-bridge/retriever';
import { initializeCleanEnvironments } from './environments/environment';
import { standaloneSourceFile } from './internal/process/functions/call/built-in/built-in-source';
import type { DataflowGraph } from './graph/graph';
import type { ControlFlowGraph } from '../util/cfg/cfg';
import { extractCFG } from '../util/cfg/cfg';
import { EdgeType } from './graph/edge';
import {
identifyLinkToLastCallRelation
} from "../queries/catalog/call-context-query/identify-link-to-last-call-relation";
} from '../queries/catalog/call-context-query/identify-link-to-last-call-relation';

export const processors: DataflowProcessors<ParentInformation> = {
[RType.Number]: processValue,
Expand Down Expand Up @@ -57,21 +58,21 @@ export const processors: DataflowProcessors<ParentInformation> = {


function resolveLinkToSideEffects(ast: NormalizedAst, graph: DataflowGraph) {
let cfg: ControlFlowGraph | undefined = undefined
let cfg: ControlFlowGraph | undefined = undefined;
for(const s of graph.unknownSideEffects) {
if(typeof s !== 'object') {
continue
continue;
}
if(!cfg) {
cfg = extractCFG(ast).graph
cfg = extractCFG(ast).graph;
}
/* this has to change whenever we add a new link to relations because we currently offer no abstraction for the type */
const potentials = identifyLinkToLastCallRelation(s.id, cfg, graph, s.linkTo.callName)
const potentials = identifyLinkToLastCallRelation(s.id, cfg, graph, s.linkTo.callName);
for(const pot of potentials) {
graph.addEdge(s.id, pot, EdgeType.Reads);
}
if(potentials.length > 0) {
graph.unknownSideEffects.delete(s)
graph.unknownSideEffects.delete(s);
}
}
}
Expand Down Expand Up @@ -103,6 +104,6 @@ export function produceDataFlowGraph<OtherInfo>(
}
}

resolveLinkToSideEffects(ast, df.graph)
resolveLinkToSideEffects(ast, df.graph);
return df;
}
10 changes: 10 additions & 0 deletions src/dataflow/graph/dataflowgraph-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { EmptyArgument } from '../../r-bridge/lang-4.x/ast/model/nodes/r-functio
import { BuiltIn } from '../environments/built-in';
import { EdgeType } from './edge';
import type { ControlDependency } from '../info';
import type { LinkTo } from '../../queries/catalog/call-context-query/call-context-query-format';
import { DefaultBuiltinConfig } from '../environments/default-builtin-config';

export function emptyGraph(idMap?: AstIdMap) {
return new DataflowGraphBuilder(idMap);
Expand Down Expand Up @@ -277,3 +279,11 @@ export class DataflowGraphBuilder extends DataflowGraph {
return this;
}
}

export function getBuiltInSideEffect(name: string): LinkTo<RegExp> | undefined {
const got = DefaultBuiltinConfig.find(e => e.names.includes(name));
if(got?.type !== 'function') {
return undefined;
}
return (got?.config as { hasUnknownSideEffects: LinkTo<RegExp> | undefined }).hasUnknownSideEffects;
}
5 changes: 4 additions & 1 deletion src/dataflow/graph/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,10 @@ function diffOutgoingEdges(ctx: DataflowDiffContext): void {

function diffRootVertices(ctx: DataflowDiffContext): void {
setDifference(ctx.left.rootIds(), ctx.right.rootIds(), { ...ctx, position: `${ctx.position}Root vertices differ in graphs. ` });
setDifference(ctx.left.unknownSideEffects, ctx.right.unknownSideEffects, { ...ctx, position: `${ctx.position}Unknown side effects differ in graphs. ` });
setDifference(
new Set([...ctx.left.unknownSideEffects].map(n => typeof n === 'object' ? n.id : n)),
new Set([...ctx.right.unknownSideEffects].map(n => typeof n === 'object' ? n.id : n)),
{ ...ctx, position: `${ctx.position}Unknown side effects differ in graphs. ` });
}


Expand Down
4 changes: 2 additions & 2 deletions src/dataflow/graph/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { cloneEnvironmentInformation } from '../environments/clone';
import { jsonReplacer } from '../../util/json';
import { BuiltIn } from '../environments/built-in';
import { dataflowLogger } from '../logger';
import {LinkTo} from "../../queries/catalog/call-context-query/call-context-query-format";
import type { LinkTo } from '../../queries/catalog/call-context-query/call-context-query-format';

export type DataflowFunctionFlowInformation = Omit<DataflowInformation, 'graph' | 'exitPoints'> & { graph: Set<NodeId> }

Expand Down Expand Up @@ -410,7 +410,7 @@ export class DataflowGraph<
/** Marks the given node as having unknown side effects */
public markIdForUnknownSideEffects(id: NodeId, target?: LinkTo<RegExp | string>): this {
if(target) {
this._unknownSideEffects.add({ id: normalizeIdToNumberIfPossible(id), linkTo: typeof target.callName === 'string' ? { ...target, callName: new RegExp(target.callName) } : target as LinkTo<RegExp> })
this._unknownSideEffects.add({ id: normalizeIdToNumberIfPossible(id), linkTo: typeof target.callName === 'string' ? { ...target, callName: new RegExp(target.callName) } : target as LinkTo<RegExp> });
return this;
}
this._unknownSideEffects.add(normalizeIdToNumberIfPossible(id));
Expand Down
4 changes: 2 additions & 2 deletions src/documentation/data/server/doc-data-server-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import {
responseQueryMessage
} from '../../../cli/repl/server/messages/message-query';
import { exampleQueryCode } from '../query/example-query-code';
import { CallTargets } from '../../../queries/catalog/call-context-query/call-context-query-format';
import { requestLineageMessage, responseLineageMessage } from '../../../cli/repl/server/messages/message-lineage';
import { CallTargets } from '../../../queries/catalog/call-context-query/identify-link-to-last-call-relation';

export function documentAllServerMessages() {

Expand Down Expand Up @@ -229,7 +229,7 @@ While the context is derived from the \`filename\`, we currently offer no way to
});

const deprecatedByQuery = `(<a href="${FlowrWikiBaseRef}/Query%20API">deprecated</a>)`;

documentServerMessage({
title: 'Slice',
type: 'request',
Expand Down
2 changes: 1 addition & 1 deletion src/documentation/print-query-wiki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
showQuery,
tocForQueryType
} from './doc-util/doc-query';
import { CallTargets } from '../queries/catalog/call-context-query/call-context-query-format';
import { describeSchema } from '../util/schema';
import { markdownFormatter } from '../util/ansi';
import { executeCallContextQueries } from '../queries/catalog/call-context-query/call-context-query-executor';
Expand All @@ -30,6 +29,7 @@ import { executeDependenciesQuery } from '../queries/catalog/dependencies-query/
import { getReplCommand } from './doc-util/doc-cli-option';
import { NewIssueUrl } from './doc-util/doc-issue';
import { executeLocationMapQuery } from '../queries/catalog/location-map-query/location-map-query-executor';
import { CallTargets } from '../queries/catalog/call-context-query/identify-link-to-last-call-relation';


registerQueryDocumentation('call-context', {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,79 +1,21 @@
import type {DataflowGraph} from '../../../dataflow/graph/graph';
import type { DataflowGraph } from '../../../dataflow/graph/graph';
import type {
CallContextQuery,
CallContextQueryKindResult,
CallContextQueryResult,
CallContextQuerySubKindResult,
SubCallContextQueryFormat
} from './call-context-query-format';
import {CallTargets} from './call-context-query-format';
import type {NodeId} from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
import {recoverContent} from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
import {VertexType} from '../../../dataflow/graph/vertex';
import {assertUnreachable} from '../../../util/assert';
import {edgeIncludesType, EdgeType} from '../../../dataflow/graph/edge';
import {resolveByName} from '../../../dataflow/environments/resolve-by-name';
import {BuiltIn} from '../../../dataflow/environments/built-in';
import {extractCFG} from '../../../util/cfg/cfg';
import {TwoLayerCollector} from '../../two-layer-collector';
import {compactRecord} from '../../../util/objects';
import {ReferenceType} from '../../../dataflow/environments/identifier';

import type {BasicQueryData} from '../../base-query-format';
import {identifyLinkToLastCallRelation} from "./identify-link-to-last-call-relation";

function satisfiesCallTargets(id: NodeId, graph: DataflowGraph, callTarget: CallTargets): NodeId[] | 'no' {
const callVertex = graph.get(id);
if(callVertex === undefined || callVertex[0].tag !== VertexType.FunctionCall) {
return 'no';
}
const [info,outgoing] = callVertex;
const callTargets = [...outgoing]
.filter(([, e]) => edgeIncludesType(e.types, EdgeType.Calls))
.map(([t]) => t)
;

let builtIn = false;

if(info.environment === undefined) {
/* if we have a call with an unbound environment,
* this only happens if we are sure of built-in relations and want to save references
*/
builtIn = true;
} else {
/*
* for performance and scoping reasons, flowR will not identify the global linkage,
* including any potential built-in mapping.
*/
const reResolved = resolveByName(info.name, info.environment, ReferenceType.Unknown);
if(reResolved?.some(t => t.definedAt === BuiltIn)) {
builtIn = true;
}
}

switch(callTarget) {
case CallTargets.Any:
return callTargets;
case CallTargets.OnlyGlobal:
if(callTargets.length === 0) {
return builtIn ? [BuiltIn] : [];
} else {
return 'no';
}
case CallTargets.MustIncludeGlobal:
return builtIn || callTargets.length === 0 ? [...callTargets, BuiltIn] : 'no';
case CallTargets.OnlyLocal:
return !builtIn && callTargets.length > 0 ? callTargets : 'no';
case CallTargets.MustIncludeLocal:
if(callTargets.length > 0) {
return builtIn ? [...callTargets, BuiltIn] : callTargets;
} else {
return 'no';
}
default:
assertUnreachable(callTarget);
}
}
import type { NodeId } from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
import { recoverContent } from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
import { VertexType } from '../../../dataflow/graph/vertex';
import { edgeIncludesType, EdgeType } from '../../../dataflow/graph/edge';
import { extractCFG } from '../../../util/cfg/cfg';
import { TwoLayerCollector } from '../../two-layer-collector';
import { compactRecord } from '../../../util/objects';

import type { BasicQueryData } from '../../base-query-format';
import { identifyLinkToLastCallRelation, satisfiesCallTargets } from './identify-link-to-last-call-relation';

/* if the node is effected by nse, we have an ingoing nse edge */
function isQuoted(node: NodeId, graph: DataflowGraph): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,7 @@ import Joi from 'joi';
import { asciiCallContext } from '../../query-print';
import type { PipelineOutput } from '../../../core/steps/pipeline/pipeline';
import type { DEFAULT_DATAFLOW_PIPELINE } from '../../../core/steps/pipeline/default-pipelines';

export enum CallTargets {
/** call targets a function that is not defined locally (e.g., the call targets a library function) */
OnlyGlobal = 'global',
/** call targets a function that is defined locally or globally, but must include a global function */
MustIncludeGlobal = 'must-include-global',
/** call targets a function that is defined locally */
OnlyLocal = 'local',
/** call targets a function that is defined locally or globally, but must include a local function */
MustIncludeLocal = 'must-include-local',
/** call targets a function that is defined locally or globally */
Any = 'any'
}
import { CallTargets } from './identify-link-to-last-call-relation';

export interface DefaultCallContextQueryFormat<CallName extends RegExp | string> extends BaseQueryFormat {
readonly type: 'call-context';
Expand Down
Loading

0 comments on commit 3847bf1

Please sign in to comment.