From a29c4b7f2bffa12a6890bb425504d93ed3938828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Tainon?= Date: Fri, 24 May 2024 17:45:13 +0200 Subject: [PATCH] Support quicksort directive in C --- frontend/stepper/abstract_runner.ts | 6 + .../analysis/abstract_variable_fetcher.ts | 47 ++++++++ .../analysis/default_variable_fetcher.ts | 48 ++++++++ .../stepper/analysis/directives/array1d.tsx | 10 +- .../analysis/directives/array2d_model.ts | 8 +- .../analysis/directives/array_utils.ts | 29 +++-- frontend/stepper/analysis/directives/sort.tsx | 27 ++--- .../stepper/analysis/directives/utils.tsx | 85 -------------- frontend/stepper/c/analysis.ts | 2 +- frontend/stepper/c/unix_runner.ts | 11 +- frontend/stepper/c/unix_variable_fetcher.ts | 106 ++++++++++++++++++ frontend/stepper/views/DirectivePanel.tsx | 30 ++--- frontend/stepper/views/c/array2d_model.ts | 2 +- frontend/stepper/views/c/array_utils.ts | 3 +- frontend/stepper/views/c/sort.tsx | 5 +- frontend/stepper/views/c/utils.tsx | 6 +- frontend/style.scss | 8 +- frontend/task/layout/LayoutDirective.tsx | 13 ++- frontend/task/layout/LayoutLoader.tsx | 1 - 19 files changed, 295 insertions(+), 152 deletions(-) create mode 100644 frontend/stepper/analysis/abstract_variable_fetcher.ts create mode 100644 frontend/stepper/analysis/default_variable_fetcher.ts create mode 100644 frontend/stepper/c/unix_variable_fetcher.ts diff --git a/frontend/stepper/abstract_runner.ts b/frontend/stepper/abstract_runner.ts index acd30e2b..cc0faf91 100644 --- a/frontend/stepper/abstract_runner.ts +++ b/frontend/stepper/abstract_runner.ts @@ -4,6 +4,8 @@ import {ContextEnrichingTypes} from './actionTypes'; import {TaskAnswer} from '../task/task_types'; import log from 'loglevel'; import {Codecast} from '../app_types'; +import AbstractVariableFetcher from './analysis/abstract_variable_fetcher'; +import DefaultVariableFetcher from './analysis/default_variable_fetcher'; export default abstract class AbstractRunner { //TODO: improve this @@ -215,4 +217,8 @@ export default abstract class AbstractRunner { const nextThreadPosition = (currentThreadPosition + 1) % threadIds.length; this.nextThreadId = Number(threadIds[nextThreadPosition]); } + + public getVariableFetcher(): AbstractVariableFetcher { + return new DefaultVariableFetcher(); + } } diff --git a/frontend/stepper/analysis/abstract_variable_fetcher.ts b/frontend/stepper/analysis/abstract_variable_fetcher.ts new file mode 100644 index 00000000..642c32ae --- /dev/null +++ b/frontend/stepper/analysis/abstract_variable_fetcher.ts @@ -0,0 +1,47 @@ +import {LayoutDirectiveContext} from '../../task/layout/LayoutDirective'; +import {CodecastAnalysisStackFrame, CodecastAnalysisVariable} from './analysis'; +import {DirectiveVariableName} from './directives/utils'; + +export default abstract class AbstractVariableFetcher { + getNumber(expr, options) { + return expr; + } + getList(expr, defaultValue) { + return expr; + } + stringifyVariableName(name) { + return name; + } + getVariable(context: LayoutDirectiveContext, name: DirectiveVariableName, elemCount?: number): CodecastAnalysisVariable { + // Check in the last (the current) and the first (which is the global) scopes. + + const variableName = Array.isArray(name) ? name[1] : name; + const analysis = context.analysis; + const nbScopes = analysis.stackFrames.length; + let variable: CodecastAnalysisVariable = null; + if (nbScopes) { + variable = this.getVariableInScope(analysis.stackFrames[nbScopes - 1], variableName); + } + if (!variable && nbScopes > 1) { + variable = this.getVariableInScope(analysis.stackFrames[0], variableName); + } + + return variable; + } + getVariables(context: LayoutDirectiveContext, names: DirectiveVariableName[]): {name: DirectiveVariableName, value: CodecastAnalysisVariable}[] { + return names.map((name) => { + return { + name, + value: this.getVariable(context, name) + } + }); + } + /** + * Gets a variable by name in a scope. + */ + getVariableInScope(stackFrame: CodecastAnalysisStackFrame, name: string): CodecastAnalysisVariable { + let variable = stackFrame.variables.find(variable => name === variable.name); + + return variable ? variable : null; + } +} diff --git a/frontend/stepper/analysis/default_variable_fetcher.ts b/frontend/stepper/analysis/default_variable_fetcher.ts new file mode 100644 index 00000000..ee93f8ea --- /dev/null +++ b/frontend/stepper/analysis/default_variable_fetcher.ts @@ -0,0 +1,48 @@ +import {LayoutDirectiveContext} from '../../task/layout/LayoutDirective'; +import {CodecastAnalysisStackFrame, CodecastAnalysisVariable} from './analysis'; +import {DirectiveVariableName} from './directives/utils'; +import AbstractVariableFetcher from './abstract_variable_fetcher'; + +export default class DefaultVariableFetcher extends AbstractVariableFetcher { + getNumber(expr, options) { + return expr; + } + getList(expr, defaultValue) { + return expr; + } + stringifyVariableName(name) { + return name; + } + getVariable(context: LayoutDirectiveContext, name: DirectiveVariableName, elemCount?: number): CodecastAnalysisVariable { + // Check in the last (the current) and the first (which is the global) scopes. + + const variableName = Array.isArray(name) ? name[1] : name; + const analysis = context.analysis; + const nbScopes = analysis.stackFrames.length; + let variable: CodecastAnalysisVariable = null; + if (nbScopes) { + variable = this.getVariableInScope(analysis.stackFrames[nbScopes - 1], variableName); + } + if (!variable && nbScopes > 1) { + variable = this.getVariableInScope(analysis.stackFrames[0], variableName); + } + + return variable; + } + getVariables(context: LayoutDirectiveContext, names: DirectiveVariableName[]): {name: DirectiveVariableName, value: CodecastAnalysisVariable}[] { + return names.map((name) => { + return { + name, + value: this.getVariable(context, name) + } + }); + } + /** + * Gets a variable by name in a scope. + */ + getVariableInScope(stackFrame: CodecastAnalysisStackFrame, name: string): CodecastAnalysisVariable { + let variable = stackFrame.variables.find(variable => name === variable.name); + + return variable ? variable : null; + } +} diff --git a/frontend/stepper/analysis/directives/array1d.tsx b/frontend/stepper/analysis/directives/array1d.tsx index 9494602e..18be4b79 100644 --- a/frontend/stepper/analysis/directives/array1d.tsx +++ b/frontend/stepper/analysis/directives/array1d.tsx @@ -8,6 +8,8 @@ import {DirectiveFrame} from '../../views/DirectiveFrame'; import {StepperControls} from "../../index"; import {CodecastAnalysisVariable} from "../analysis"; import {LayoutDirectiveContext} from '../../../task/layout/LayoutDirective'; +import {Codecast} from '../../../app_types'; +import {getList} from '../../views/c/utils'; const TEXT_LINE_HEIGHT = 18; const TEXT_BASELINE = 5; // from bottom @@ -165,14 +167,14 @@ export class Array1D extends React.PureComponent { const cellPan = this.getPosition(); const {byName, byPos} = directive; - const cursors = (byName.cursors) ? byName.cursors : []; + const cursors = byName.cursors ? context.variableFetcher.getList(byName.cursors, []) : []; - const cursorRows = (byName.cursorRows) ? byName.cursorRows : DEFAULT_CURSOR_ROWS; + const cursorRows = byName.cursorRows ? context.variableFetcher.getNumber(byName.cursorRows, DEFAULT_CURSOR_ROWS) : DEFAULT_CURSOR_ROWS; const cellHeight = (3 + cursorRows) * TEXT_LINE_HEIGHT + MIN_ARROW_HEIGHT; - const cellWidth = this._cellWidth = (byName.cw) ? byName.cw : DEFAULT_CELL_WIDTH; + const cellWidth = this._cellWidth = byName.cw ? context.variableFetcher.getNumber(byName.cw, DEFAULT_CELL_WIDTH) : DEFAULT_CELL_WIDTH; - const maxVisibleCells = (byName.n) ? byName.n : DEFAULT_MAX_VISIBLE_CELLS; + const maxVisibleCells = byName.n ? context.variableFetcher.getNumber(byName.n, DEFAULT_MAX_VISIBLE_CELLS) : DEFAULT_MAX_VISIBLE_CELLS; const {dim} = byName; diff --git a/frontend/stepper/analysis/directives/array2d_model.ts b/frontend/stepper/analysis/directives/array2d_model.ts index 32081ec1..d7f79ca1 100644 --- a/frontend/stepper/analysis/directives/array2d_model.ts +++ b/frontend/stepper/analysis/directives/array2d_model.ts @@ -1,16 +1,16 @@ -import {DirectiveVariableName, getVariable} from './utils'; +import {DirectiveVariableName} from './utils'; import {getCursorMap} from './array_utils'; import {getMessage} from "../../../lang"; import {LayoutDirectiveContext} from '../../../task/layout/LayoutDirective'; export const extractView = function (context: LayoutDirectiveContext, name: DirectiveVariableName, options) { - const ref = getVariable(context, name); + const ref = context.variableFetcher.getVariable(context, name); if (!ref) { - return {error: getMessage('ARRAY2D_REF_UNDEFINED').format({name})}; + return {error: getMessage('ARRAY2D_REF_UNDEFINED').format({name: context.variableFetcher.stringifyVariableName(name)})}; } if (!ref.variables) { - return {error: getMessage('ARRAY2D_REF_NOT_LIST').format({name})}; + return {error: getMessage('ARRAY2D_REF_NOT_LIST').format({name: context.variableFetcher.stringifyVariableName(name)})}; } const rowCount = (options.rowCount) ? options.rowCount : ref.variables.length; diff --git a/frontend/stepper/analysis/directives/array_utils.ts b/frontend/stepper/analysis/directives/array_utils.ts index 0afe536f..1d873f3c 100644 --- a/frontend/stepper/analysis/directives/array_utils.ts +++ b/frontend/stepper/analysis/directives/array_utils.ts @@ -1,8 +1,9 @@ import range from 'node-range'; -import {DirectiveVariableName, getVariable} from './utils'; +import {DirectiveVariableName} from './utils'; import {getMessage} from "../../../lang"; import {LayoutDirectiveContext} from '../../../task/layout/LayoutDirective'; +import {stringifyExpr} from '../../views/c/utils'; /** extractView(context, name, options) looks up `name` in `stackFrame` and @@ -61,7 +62,7 @@ import {LayoutDirectiveContext} from '../../../task/layout/LayoutDirective'; export const extractView = function (context: LayoutDirectiveContext, name, options) { // Normalize options. const {dim} = options; - let {cursors, cursorRows, maxVisibleCells, pointsByKind} = options; + let {cursors, cursorRows, maxVisibleCells, pointsByKind, thresholds} = options; if (cursors === undefined) { cursors = []; } @@ -87,20 +88,22 @@ export const extractView = function (context: LayoutDirectiveContext, name, opti if (/^\d/.test(dim)) { elemCount = dim; } else { - const dimVariable = getVariable(context, dim); + const dimVariable = context.variableFetcher.getVariable(context, dim); if (dimVariable && dimVariable.value) { elemCount = Number(dimVariable.value); + } else { + return {error: getMessage('ARRAY1D_DIM_INVALID').format({dim: stringifyExpr(dim)})}; } } } - const ref = getVariable(context, name, elemCount); + const ref = context.variableFetcher.getVariable(context, name, elemCount); if (!ref) { - return {error: getMessage('ARRAY1D_REF_UNDEFINED').format({name})}; + return {error: getMessage('ARRAY1D_REF_UNDEFINED').format({name: context.variableFetcher.stringifyVariableName(name)})}; } if (!ref.variables) { - return {error: getMessage('ARRAY1D_REF_NOT_LIST').format({name})}; + return {error: getMessage('ARRAY1D_REF_NOT_LIST').format({name: context.variableFetcher.stringifyVariableName(name)})}; } if (elemCount === undefined) { @@ -112,12 +115,15 @@ export const extractView = function (context: LayoutDirectiveContext, name, opti maxIndex: elemCount }); + const thresholdsMap = thresholds ? getCursorMap(context, thresholds, {}) : []; + const selection = range(0, elemCount + 1); finalizeCursors(selection, cursorMap, cursorRows); return { ref, cursorMap, + thresholdsMap, }; }; @@ -133,7 +139,7 @@ export const getCursorMap = function (context: LayoutDirectiveContext, cursorNam const cursorMap = []; // spare array cursorNames.forEach(function(name) { - const cursorVariable = getVariable(context, name); + const cursorVariable = context.variableFetcher.getVariable(context, name); if (!cursorVariable) { return; } @@ -142,18 +148,19 @@ export const getCursorMap = function (context: LayoutDirectiveContext, cursorNam return; } + const cursorLabel = context.variableFetcher.stringifyVariableName(name); + const index = cursorVariable.value; - if (index >= minIndex && index <= maxIndex) { - // TODO: We currently do not attempt to find the previous value of the cursor (and when it changed) ? + if (undefined === minIndex || undefined === maxIndex || (index >= minIndex && index <= maxIndex)) { if (!(index in cursorMap)) { cursorMap[index] = { index, - labels: [] + labels: [], }; } - cursorMap[index].labels.push(name); + cursorMap[index].labels.push(cursorLabel); } }); diff --git a/frontend/stepper/analysis/directives/sort.tsx b/frontend/stepper/analysis/directives/sort.tsx index c4e15f46..6b89beab 100644 --- a/frontend/stepper/analysis/directives/sort.tsx +++ b/frontend/stepper/analysis/directives/sort.tsx @@ -1,11 +1,10 @@ import React from 'react'; -import {getVariables, renderArrow, renderValue} from './utils'; +import {renderArrow, renderValue} from './utils'; import {extractView} from './array_utils'; import {SvgPan} from '../../views/SvgPan'; import {DirectiveFrame} from "../../views/DirectiveFrame"; import {StepperControls} from "../../index"; -import {CodecastAnalysisVariable} from "../analysis"; import {LayoutDirectiveContext} from '../../../task/layout/LayoutDirective'; const DEFAULT_MAX_VISIBLE_CELLS = 40; @@ -34,7 +33,8 @@ interface SortViewParams { nbCells?: any, ref?: any, maxValue?: any, - cursorMap?: any + cursorMap?: any, + thresholdsMap?: any, } function getValueClass(view, index) { @@ -123,22 +123,22 @@ interface ThresholdProps { } function Threshold({view, threshold}: ThresholdProps) { - const {name, value}: {name: string, value: CodecastAnalysisVariable} = threshold; + const {index, labels}: {index: string, labels: string[]} = threshold; - if (!value || !value.value) { + if (!index) { return null; } const x0 = MARGIN_LEFT - THRESHOLD_LINE_EXT; const x1 = MARGIN_LEFT + (BAR_WIDTH + BAR_SPACING) * view.nbCells + THRESHOLD_LINE_EXT; const x2 = MARGIN_LEFT - THRESHOLD_MARGIN_RIGHT; - const y0 = MARGIN_TOP + BAR_HEIGHT * Number(value.value) / view.maxValue; + const y0 = MARGIN_TOP + BAR_HEIGHT * Number(index) / view.maxValue; const y1 = y0 + TEXT_BASELINE; return ( - {name} + {labels.join(',')} ); } @@ -183,16 +183,17 @@ export class SortView extends React.PureComponent { const {dim} = byName; const cellPan = this.getPosition(); - const thresholds = (byName.thresholds) ? byName.thresholds : []; - const cursors = (byName.cursors) ? byName.cursors : []; - const cursorRows = (byName.cursorRows) ? byName.cursorRows : 1; - const maxVisibleCells = (byName.n) ? byName.n : DEFAULT_MAX_VISIBLE_CELLS; + const thresholds = byName.thresholds ? context.variableFetcher.getList(byName.thresholds, []) : []; + const cursors = byName.cursors ? context.variableFetcher.getList(byName.cursors, []) : []; + const cursorRows = byName.cursorRows ? context.variableFetcher.getNumber(byName.cursorRows, 1) : 1; + const maxVisibleCells = byName.n ? context.variableFetcher.getNumber(byName.n, DEFAULT_MAX_VISIBLE_CELLS) : DEFAULT_MAX_VISIBLE_CELLS; const svgHeight = MARGIN_TOP + BAR_HEIGHT + BAR_MARGIN_BOTTOM + TEXT_LINE_HEIGHT + MIN_ARROW_HEIGHT + TEXT_LINE_HEIGHT * cursorRows + MARGIN_BOTTOM; const view: SortViewParams = { dim, fullView, cursors, + thresholds, maxVisibleCells, cursorRows, }; @@ -201,7 +202,7 @@ export class SortView extends React.PureComponent { return {view.error}; } - view.thresholds = getVariables(context, thresholds); + view.thresholds = context.variableFetcher.getVariables(context, thresholds); const list = view.ref.variables; view.nbCells = list.length; @@ -227,7 +228,7 @@ export class SortView extends React.PureComponent { {list.map((element, index) => )} - {view.thresholds.map((threshold, i) => )} diff --git a/frontend/stepper/analysis/directives/utils.tsx b/frontend/stepper/analysis/directives/utils.tsx index e2ef1a24..fbed5a34 100644 --- a/frontend/stepper/analysis/directives/utils.tsx +++ b/frontend/stepper/analysis/directives/utils.tsx @@ -1,92 +1,7 @@ import React from 'react'; -import { - CodecastAnalysisStackFrame, - CodecastAnalysisVariable, convertVariableDAPToCodecastFormat -} from "../analysis"; -import {LayoutDirectiveContext} from '../../../task/layout/LayoutDirective'; -import {evalExpr, readValue, stringifyExpr} from '../../views/c/utils'; -import {convertUnixValueToDAPVariable} from '../../c/analysis'; -import * as C from '@france-ioi/persistent-c'; -import {getMessage} from '../../../lang'; -import {getOpsArray1D} from '../../views/c/array_utils'; export type DirectiveVariableName = string|[string, string]; -export const getVariable = function (context: LayoutDirectiveContext, name: DirectiveVariableName, elemCount?: number): CodecastAnalysisVariable { - // Check in the last (the current) and the first (which is the global) scopes. - - const variableName = Array.isArray(name) ? name[1] : name; - const analysis = context.analysis; - const nbScopes = analysis.stackFrames.length; - let variable: CodecastAnalysisVariable = null; - if (nbScopes) { - variable = getVariableInScope(analysis.stackFrames[nbScopes - 1], variableName); - } - if (!variable && nbScopes > 1) { - variable = getVariableInScope(analysis.stackFrames[0], variableName); - } - - if (variable && undefined !== elemCount) { - const programState = context.programState; - const unixVariable = variable.unixVariable; - const localMap = new Map([[variableName, unixVariable]]); - const refExpr = name; - const cursorValue = evalExpr(programState, localMap, refExpr, false); - console.log('get var', {name, variable, unixVariable, analysis: analysis.stackFrames, nbScopes, elemCount, cursorValue}) - - const {type, ref} = unixVariable; - // const limits = {scalars: 0, maxScalars: 15}; - // const value = readValue(context, C.pointerType(type), ref.address, limits); - - if (ref.type.kind !== 'pointer') { - throw getMessage('ARRAY1D_EXPR_NOPTR').format({expr: stringifyExpr(refExpr)}); - } - if (elemCount === undefined) { - if ('orig' in ref.type) { - // The array size can be obtained from the original type. - elemCount = ref.type.orig.count.toInteger(); - } else { - throw getMessage('ARRAY1D_DIM_UNK').format({expr: stringifyExpr(refExpr)}); - } - } - const address = ref.address; - const elemType = ref.type.pointee; - if (!/^(builtin|pointer|array)$/.test(elemType.kind)) { - throw getMessage('ARRAY1D_ELT_UNSUP').format({expr: stringifyExpr(refExpr)}); - } - const cellOpsMap = getOpsArray1D(programState, address, elemCount, elemType.size); - - const typeDecl = ''; - // const typeDecl = renderDeclType(type, '', 0); - - // const convertedVariable = convertUnixValueToDAPVariable(variableName, typeDecl, value, ref.address, {}); - console.log('converted variable', cellOpsMap); - - // return convertedVariable; - - } - - return variable; -}; - -export const getVariables = function (context: LayoutDirectiveContext, names: DirectiveVariableName[]): {name: DirectiveVariableName, value: CodecastAnalysisVariable}[] { - return names.map((name) => { - return { - name, - value: getVariable(context, name) - } - }); -}; - -/** - * Gets a variable by name in a scope. - */ -const getVariableInScope = function(stackFrame: CodecastAnalysisStackFrame, name: string): CodecastAnalysisVariable { - let variable = stackFrame.variables.find(variable => name === variable.name); - - return variable ? variable : null; -}; - export const renderValue = function(value) { if (value === undefined) { return 'noval'; diff --git a/frontend/stepper/c/analysis.ts b/frontend/stepper/c/analysis.ts index cba92be5..5e97c7b2 100644 --- a/frontend/stepper/c/analysis.ts +++ b/frontend/stepper/c/analysis.ts @@ -147,7 +147,7 @@ function convertUnixStackFrameToAnalysisStackFrame(unixStackFrame: StackFrameUni }; } -function renderDeclType(type, subject, prec): string { +export function renderDeclType(type, subject, prec): string { switch (type.kind) { case 'function': // TODO: print param types? diff --git a/frontend/stepper/c/unix_runner.ts b/frontend/stepper/c/unix_runner.ts index 9fc630eb..de60c75d 100644 --- a/frontend/stepper/c/unix_runner.ts +++ b/frontend/stepper/c/unix_runner.ts @@ -17,6 +17,7 @@ import {getContextBlocksDataSelector} from '../../task/blocks/blocks'; import {quickAlgoLibraries} from '../../task/libs/quick_algo_libraries_model'; import {convertAnalysisDAPToCodecastFormat} from '../analysis/analysis'; import {parseDirectives} from '../python/directives'; +import UnixVariableFetcher from './unix_variable_fetcher'; const RETURN_TYPE_CONVERSION = { 'bool': 'int', @@ -131,14 +132,8 @@ export default class UnixRunner extends AbstractRunner { stepperState.isFinished = !stepperState.programState.control; stepperState.analysis = convertUnixStateToAnalysisSnapshot(stepperState.programState, stepperState.lastProgramState); - const focusDepth = controls.stack.focusDepth; - const analysisBack = analyseState(programState); - console.log('stepper state directives', stepperState.directives, {analysisBack, focusDepth}); - stepperState.directives = parseDirectives(stepperState.analysis); - console.log('stepper state directives 2', stepperState.directives); - if (!stepperState.lastAnalysis) { stepperState.lastAnalysis = { stackFrames: [], @@ -260,4 +255,8 @@ export default class UnixRunner extends AbstractRunner { } } } + + public getVariableFetcher() { + return new UnixVariableFetcher(); + } } diff --git a/frontend/stepper/c/unix_variable_fetcher.ts b/frontend/stepper/c/unix_variable_fetcher.ts new file mode 100644 index 00000000..40100531 --- /dev/null +++ b/frontend/stepper/c/unix_variable_fetcher.ts @@ -0,0 +1,106 @@ +import AbstractVariableFetcher from '../analysis/abstract_variable_fetcher'; +import * as C from '@france-ioi/persistent-c'; +import {evalExpr, stringifyExpr} from '../views/c/utils'; +import {LayoutDirectiveContext} from '../../task/layout/LayoutDirective'; +import {DirectiveVariableName} from '../analysis/directives/utils'; +import {CodecastAnalysisVariable, convertVariableDAPToCodecastFormat} from '../analysis/analysis'; +import {getMessage} from '../../lang'; +import {getOpsArray1D, readArray1D} from '../views/c/array_utils'; +import {convertUnixValueToDAPVariable} from './analysis'; +import range from 'node-range'; + +export default class UnixVariableFetcher extends AbstractVariableFetcher { + getNumber(expr, options) { + let noVal; + if (typeof options === 'object') { + noVal = options.noVal; + } else { + noVal = options; + options = {}; + } + if (!expr) { + return noVal; + } + if (expr[0] === 'number') { + return expr[1]; + } + const programState = options.programState; + const stackFrame = options.stackFrame; + if (expr[0] === 'ident' && programState && stackFrame) { + const decl = stackFrame['localMap'][expr[1]]; + if (decl && decl.type.kind === 'builtin') { + const value = C.readValue(programState, decl.ref); + if (value) { + return value.toInteger(); + } + } + } + + return noVal; + } + getList(expr, defaultValue) { + if (!expr) { + return defaultValue; + } + return expr[0] === 'list' ? expr[1] : defaultValue; + } + stringifyVariableName(name) { + return stringifyExpr(name, 0); + } + getVariable(context: LayoutDirectiveContext, name: DirectiveVariableName, elemCount?: number): CodecastAnalysisVariable { + // Check in the last (the current) and the first (which is the global) scopes. + + const variableName = Array.isArray(name) ? name[1] : name; + const analysis = context.analysis; + const nbScopes = analysis.stackFrames.length; + const variable = super.getVariable(context, name, elemCount); + + if (variable && undefined !== elemCount) { + const programState = context.programState; + const unixVariable = variable.unixVariable; + const localMap = {}; + localMap[variableName] = unixVariable; + const refExpr = name; + const ref = evalExpr(programState, localMap, refExpr, false); + + if (ref.type.kind !== 'pointer') { + throw getMessage('ARRAY1D_EXPR_NOPTR').format({expr: stringifyExpr(refExpr)}); + } + if (elemCount === undefined) { + if ('orig' in ref.type) { + // The array size can be obtained from the original type. + elemCount = ref.type.orig.count.toInteger(); + } else { + throw getMessage('ARRAY1D_DIM_UNK').format({expr: stringifyExpr(refExpr)}); + } + } + const address = ref.address; + const elemType = ref.type.pointee; + if (!/^(builtin|pointer|array)$/.test(elemType.kind)) { + throw getMessage('ARRAY1D_ELT_UNSUP').format({expr: stringifyExpr(refExpr)}); + } + const cellOpsMap = getOpsArray1D(programState, address, elemCount, elemType.size); + + const selection = range(0, elemCount); + const cells = readArray1D(context, address, elemType, elemCount, selection, cellOpsMap); + + const unixValue = { + kind: 'array', + cells, + } + + const convertedVariable = convertUnixValueToDAPVariable(variableName, null, unixValue, address, {}); + + const previousValues = {}; + for (let [index, cell] of cells.entries()) { + if (cell.content.previous) { + previousValues[`0.0.${variableName}.${index}`] = cell.content.previous; + } + } + + return convertVariableDAPToCodecastFormat(0, 0, null, convertedVariable, previousValues, {}); + } + + return variable; + } +} diff --git a/frontend/stepper/views/DirectivePanel.tsx b/frontend/stepper/views/DirectivePanel.tsx index 9ced3cb9..67afa3fc 100644 --- a/frontend/stepper/views/DirectivePanel.tsx +++ b/frontend/stepper/views/DirectivePanel.tsx @@ -1,8 +1,11 @@ import React from "react"; import {DirectiveFrame} from "./DirectiveFrame"; -import {analysisDirectiveViewDict} from "./index"; +import {analysisDirectiveViewDict, C_directiveViewDict} from "./index"; +import {CodecastPlatform} from '../codecast_platform'; +import {useAppSelector} from '../../hooks'; export function DirectivePanel({scale, directive, controls, context, onChange, allocatedWidth, allocatedHeight}) { + const platform = useAppSelector(state => state.options.platform); const {kind} = directive; const hide = controls.hide; if (hide) { @@ -13,19 +16,20 @@ export function DirectivePanel({scale, directive, controls, context, onChange, a } let directiveDescription; - // if (platform === CodecastPlatform.Python) { - if (!analysisDirectiveViewDict[kind]) { - return

{'Error: undefined view kind '}{kind}

; - } + const useCDirectives = false; + if (!useCDirectives) { + if (!analysisDirectiveViewDict[kind]) { + return

{'Error: undefined view kind '}{kind}

; + } + + directiveDescription = analysisDirectiveViewDict[kind]; + } else { + if (!C_directiveViewDict[kind]) { + return

{'Error: undefined view kind '}{kind}

; + } - directiveDescription = analysisDirectiveViewDict[kind]; - // } else { - // if (!C_directiveViewDict[kind]) { - // return

{'Error: undefined view kind '}{kind}

; - // } - // - // directiveDescription = C_directiveViewDict[kind]; - // } + directiveDescription = C_directiveViewDict[kind]; + } const props = directiveDescription.selector({scale, directive, context, controls, allocatedWidth, allocatedHeight}); diff --git a/frontend/stepper/views/c/array2d_model.ts b/frontend/stepper/views/c/array2d_model.ts index f5ab6cf3..66d27a62 100644 --- a/frontend/stepper/views/c/array2d_model.ts +++ b/frontend/stepper/views/c/array2d_model.ts @@ -6,7 +6,7 @@ import {getMessage, getMessageFormat} from "../../../lang"; export const extractView = function(context, stackFrame, refExpr, options) { const {programState} = context; - const localMap = stackFrame.get('localMap'); + const localMap = stackFrame['localMap']; let ref; try { ref = evalExpr(programState, localMap, refExpr, false); diff --git a/frontend/stepper/views/c/array_utils.ts b/frontend/stepper/views/c/array_utils.ts index 7fbc73c7..546d6b4b 100644 --- a/frontend/stepper/views/c/array_utils.ts +++ b/frontend/stepper/views/c/array_utils.ts @@ -71,7 +71,7 @@ interface HeapNode { */ export const extractView = function(context, stackFrame, refExpr, options) { const {programState} = context; - const localMap = stackFrame.get('localMap'); + const localMap = stackFrame['localMap']; // Normalize options. const {fullView, dimExpr} = options; let {cursorExprs, cursorRows, maxVisibleCells, pointsByKind} = options; @@ -438,7 +438,6 @@ export const getOpsArray1D = function(programState, address, elemCount, elemSize // array cells indexes, and save the cell load/store operations in cellOps. const cellOpsMap = []; const forEachCell = mapArray1D(address, elemCount, elemSize); - console.log('foreachcell', forEachCell); programState.memoryLog.forEach(function(entry, i) { const op = entry[0]; // 'load' or 'store' forEachCell(entry[1], function(index) { diff --git a/frontend/stepper/views/c/sort.tsx b/frontend/stepper/views/c/sort.tsx index 5ba79e87..e1094698 100644 --- a/frontend/stepper/views/c/sort.tsx +++ b/frontend/stepper/views/c/sort.tsx @@ -5,6 +5,7 @@ import {extractView} from './array_utils'; import {SvgPan} from '../SvgPan'; import {DirectiveFrame} from "../DirectiveFrame"; import {StepperControls} from "../../index"; +import {analyseState} from '../../c/analysis'; const marginLeft = 100; const marginTop = 4; @@ -132,6 +133,7 @@ function Threshold({view, threshold}: ThresholdProps) { interface SortViewProps { controls: StepperControls, directive: any, + functionCallStack: any, context: any, scale: any, onChange: Function @@ -142,7 +144,8 @@ export class SortView extends React.PureComponent { render() { const {controls, directive, context, scale} = this.props; const {programState} = context; - const topStackFrame = null; + const functionCallStack = analyseState(programState).functionCallStack; + const topStackFrame = functionCallStack[functionCallStack.length - 1]; // Controls // - fullView: read and render all cells diff --git a/frontend/stepper/views/c/utils.tsx b/frontend/stepper/views/c/utils.tsx index 7c3b2912..ceb09f9e 100644 --- a/frontend/stepper/views/c/utils.tsx +++ b/frontend/stepper/views/c/utils.tsx @@ -145,7 +145,7 @@ export const refsIntersect = function(ref1, ref2) { export const evalExpr = function(programState, localMap, expr, asRef?) { if (expr[0] === 'ident') { const name = expr[1]; - const decl = localMap.get(name); + const decl = localMap[name]; if (!decl) { if (name in programState.globalMap) { const value = programState.globalMap[name]; @@ -245,7 +245,7 @@ export const stringifyExpr = function(expr, precedence?) { }; export const viewExprs = function(programState, stackFrame, exprs) { - const localMap = stackFrame.get('localMap'); + const localMap = stackFrame['localMap']; const views = []; exprs.forEach(function(expr) { const label = stringifyExpr(expr, 0); @@ -286,7 +286,7 @@ export const getNumber = function(expr, options) { const programState = options.programState; const stackFrame = options.stackFrame; if (expr[0] === 'ident' && programState && stackFrame) { - const decl = stackFrame.get('localMap').get(expr[1]); + const decl = stackFrame['localMap'][expr[1]]; if (decl && decl.type.kind === 'builtin') { const value = C.readValue(programState, decl.ref); if (value) { diff --git a/frontend/style.scss b/frontend/style.scss index 2f0293b7..48386293 100644 --- a/frontend/style.scss +++ b/frontend/style.scss @@ -567,8 +567,8 @@ svg { .sort-view { .bar rect { fill: #f0f0f0; - &.load { fill: #DCEDC8; } - &.store { fill: #FFCDD2; } + &.load { fill: #b8ceee; } + &.store { fill: #ffdb95; } stroke: #777; stroke-width: 0.5; } @@ -588,10 +588,6 @@ svg { stroke: #444; stroke-width: 1; } - &.load text { fill: #8BC34A; } - &.load line { stroke: #8BC34A; } - &.store text { fill: #F44336; } - &.store line { stroke: #F44336; } } } diff --git a/frontend/task/layout/LayoutDirective.tsx b/frontend/task/layout/LayoutDirective.tsx index 41bc12dd..41d5bbef 100644 --- a/frontend/task/layout/LayoutDirective.tsx +++ b/frontend/task/layout/LayoutDirective.tsx @@ -11,6 +11,9 @@ import {CodecastAnalysisSnapshot} from "../../stepper/analysis/analysis"; import log from 'loglevel'; import {CodecastPlatform} from '../../stepper/codecast_platform'; +import AbstractRunner from '../../stepper/abstract_runner'; +import {Codecast} from '../../app_types'; +import AbstractVariableFetcher from '../../stepper/analysis/abstract_variable_fetcher'; interface LayoutDirectiveProps { directive: any, @@ -21,6 +24,7 @@ export interface LayoutDirectiveContext { analysis: CodecastAnalysisSnapshot, programState: any, lastProgramState: any, + variableFetcher: AbstractVariableFetcher, } export function LayoutDirective(props: LayoutDirectiveProps) { @@ -41,7 +45,14 @@ export function LayoutDirective(props: LayoutDirectiveProps) { } const {codecastAnalysis, programState, lastProgramState, controls, platform} = stepperState; - const context: LayoutDirectiveContext = {analysis: codecastAnalysis, programState, lastProgramState}; + + const variableFetcher = Codecast.runner.getVariableFetcher(); + const context: LayoutDirectiveContext = { + analysis: codecastAnalysis, + programState, + lastProgramState, + variableFetcher, + }; const {key} = props.directive; const dirControls = (controls.hasOwnProperty(key)) ? controls[key] : initialStepperStateControls; diff --git a/frontend/task/layout/LayoutLoader.tsx b/frontend/task/layout/LayoutLoader.tsx index b6144479..4e8ae03e 100644 --- a/frontend/task/layout/LayoutLoader.tsx +++ b/frontend/task/layout/LayoutLoader.tsx @@ -3,7 +3,6 @@ import {connect} from "react-redux"; import {AppStore, CodecastOptions} from "../../store"; import {createLayout, selectActiveView} from "./layout"; import {StepperStatus} from "../../stepper"; -import {ActionTypes} from "./actionTypes"; import {withResizeDetector} from 'react-resize-detector'; import {Directive} from "../../stepper/python/directives"; import {Screen} from "../../common/screens";