refactored static analysis to improve performance

pull/5370/head
tizah 4 years ago
parent fdb733b5bc
commit 247e51898d
  1. 45
      libs/remix-ui/static-analyser/src/lib/Checkbox/StaticAnalyserCheckedBox.tsx
  2. 3
      libs/remix-ui/static-analyser/src/lib/ErrorRenderer.tsx
  3. 16
      libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts
  4. 65
      libs/remix-ui/static-analyser/src/lib/reducers/staticAnalysisReducer.ts
  5. 163
      libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx

@ -1,45 +0,0 @@
import React from 'react' //eslint-disable-line
interface StaticAnalyserCheckBoxProps {
onClick?: (event) => void
onChange?: (event) => void
label?: string
inputType?: string
name?: string
checked?: boolean
id?: string
itemName?: string
categoryId?: string
}
const StaticAnalyserCheckedBox = ({
id,
label,
onClick,
inputType,
name,
checked,
onChange,
itemName,
categoryId
}: StaticAnalyserCheckBoxProps) => {
return (
<div className="listenOnNetwork_2A0YE0 custom-control custom-checkbox" style={{ display: 'flex', alignItems: 'center' }} onClick={onClick}>
<input
id={id}
type={inputType}
onChange={onChange}
style={{ verticalAlign: 'bottom' }}
name={name}
className="custom-control-input"
checked={checked}
/>
<label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{ paddingTop: '0.15rem' }}>
{name ? <div className="font-weight-bold">{itemName}</div> : ''}
{label}
</label>
</div>
)
}
export default StaticAnalyserCheckedBox

@ -1,4 +1,4 @@
import React, { useState } from 'react' //eslint-disable-line
import React from 'react' //eslint-disable-line
interface ErrorRendererProps {
message: any;
@ -32,6 +32,7 @@ const ErrorRenderer = ({ message, opt, editor }: ErrorRendererProps) => {
if (!message) return
let position = getPositionDetails(message)
console.log({ position })
if (!position.errFile || (opt.errorType && opt.errorType === position.errFile)) {
// Updated error reported includes '-->' before file details
const errorDetails = message.split('-->')

@ -1,24 +1,14 @@
import React, { useState } from 'react' //eslint-disable-line
import React from 'react' //eslint-disable-line
export const compilation = (analysisModule, state, run) => {
// const setCompilationResult = async (data, source, file) => {
// await setResult({ lastCompilationResult: data, lastCompilationSource: source, currentFile: file })
// }
export const compilation = (analysisModule, dispatch) => {
if (analysisModule) {
analysisModule.on(
'solidity',
'compilationFinished',
(file, source, languageVersion, data) => {
if (languageVersion.indexOf('soljson') !== 0) return
setCompilationResult(data, source, file)
if (state.categoryIndex.length > 0) {
run(data, source, file)
}
dispatch({ type: 'compilationFinished', payload: { file, source, languageVersion, data } })
}
)
}
}
export const setCompilationResult = async (data, source, file) => {
return await { data, source, file }
}

@ -1,65 +1,22 @@
import remixLib from '@remix-project/remix-lib'
import * as _ from 'lodash'
const StaticAnalysisRunner = require('@remix-project/remix-analyzer').CodeAnalysis
const utils = remixLib.util
const runner = new StaticAnalysisRunner()
const preProcessModules = (arr: any) => {
return arr.map((Item, i) => {
const itemObj = new Item()
itemObj._index = i
itemObj.categoryDisplayName = itemObj.category.displayName
itemObj.categoryId = itemObj.category.id
return itemObj
})
}
const groupedModules = utils.groupBy(
preProcessModules(runner.modules()),
'categoryId'
)
const getIndex = (modules, array) => {
Object.values(modules).map((value: {_index}) => {
if (Array.isArray(value)) {
value.forEach((x) => {
array.push(x._index.toString())
})
} else {
array.push(value._index.toString())
}
})
}
const groupedModuleIndex = (modules) => {
const indexOfCategory = []
if (!_.isEmpty(modules)) {
getIndex(modules, indexOfCategory)
}
return indexOfCategory
export const initialState = {
file: null,
source: null,
languageVersion: null,
data: null
}
export const initialState = { categoryIndex: [] }
export const analysisReducer = (state, action) => {
switch (action.type) {
case 'initialize':
return { ...state, categoryIndex: groupedModuleIndex(groupedModules) }
case 'uncheck':
case 'compilationFinished':
return {
...state,
categoryIndex: state.categoryIndex.filter((el) => {
return !action.payload.includes(el)
})
file: action.payload.file,
source: action.payload.source,
languageVersion: action.payload.languageVersion,
data: action.payload.data
}
case 'check':
return { ...state, categoryIndex: _.uniq([...state.categoryIndex, ...action.payload]) }
case 'uncheckSingle':
return { ...state, categoryIndex: state.categoryIndex.filter(val => val !== action.payload) }
case 'checkSingle':
return { ...state, categoryIndex: _.uniq([...state.categoryIndex, action.payload]) }
default:
return { ...state, categoryIndex: groupedModuleIndex(groupedModules) }
return initialState
}
}

@ -1,30 +1,20 @@
import React, { useEffect, useState, useReducer } from 'react'
import ReactDOM from 'react-dom' //eslint-disable-line
import CheckBox from './Checkbox/StaticAnalyserCheckedBox' // eslint-disable-line
import Button from './Button/StaticAnalyserButton' // eslint-disable-line
import remixLib from '@remix-project/remix-lib'
import _ from 'lodash'
import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line
import { RemixUiCheckbox } from '@remix-ui/checkbox' // eslint-disable-line
import ErrorRenderer from './ErrorRenderer' // eslint-disable-line
import { compilation, setCompilationResult } from './actions/staticAnalysisActions'
import { analysisReducer, initialState } from './reducers/staticAnalysisReducer'
import { compilation } from './actions/staticAnalysisActions'
import { initialState, analysisReducer } from './reducers/staticAnalysisReducer'
const StaticAnalysisRunner = require('@remix-project/remix-analyzer').CodeAnalysis
const utils = remixLib.util
/* eslint-disable-next-line */
export interface RemixUiStaticAnalyserProps {
renderStaticAnalysis: any
staticanalysis: any
analysisRunner: any,
lastCompilationResult: any,
lastCompilationSource: any,
registry: any,
event: any,
analysisModule: any
_deps: any,
emit: any
}
export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
@ -65,88 +55,46 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
return indexOfCategory
}
const [autoRun, setAutoRun] = useState(true)
// const initialState = { categoryIndex: [] }
// const reducer = (state, action) => {
// console.log({ action })
// switch (action.type) {
// case 'initialize':
// return { categoryIndex: groupedModuleIndex(groupedModules) }
// case 'uncheck':
// return {
// categoryIndex: state.categoryIndex.filter((el) => {
// return !action.payload.includes(el)
// })
// }
// case 'check':
// return { categoryIndex: _.uniq([...state.categoryIndex, ...action.payload]) }
// case 'uncheckSingle':
// return { categoryIndex: state.categoryIndex.filter(val => val !== action.payload) }
// case 'checkSingle':
// return { categoryIndex: _.uniq([...state.categoryIndex, action.payload]) }
// default:
// return { categoryIndex: groupedModuleIndex(groupedModules) }
// }
// }
const [state, dispatch] = useReducer(analysisReducer, initialState)
const [categoryIndex, setCategoryIndex] = useState(groupedModuleIndex(groupedModules))
const warningContainer = React.useRef(null)
const [runButtonState, setRunButtonState] = useState(true)
const [result, setResult] = useState({
lastCompilationResult: null,
lastCompilationSource: null,
currentFile: 'No file compiled'
})
const [, setModuleNameResult] = useState(null)
const [, setWarning] = useState({
options: {},
hasWarning: false,
warningErrors: []
})
const [warningState, setWarningState] = useState([])
const [state, dispatch] = useReducer(analysisReducer, initialState)
// const executeCompilation = (categoryIndex) => {
// const setCompilationResult = async (data, source, file) => {
// await setResult({ lastCompilationResult: data, lastCompilationSource: source, currentFile: file })
// }
// if (props.analysisModule) {
// props.analysisModule.on(
// 'solidity',
// 'compilationFinished',
// (file, source, languageVersion, data) => {
// if (languageVersion.indexOf('soljson') !== 0) return
// setCompilationResult(data, source, file)
// console.log(categoryIndex, ' inside execute funtions')
// if (state.categoryIndex.length > 0) {
// run(data, source, file)
// }
// }
// )
// }
// }
useEffect(() => {
compilation(props.analysisModule, dispatch)
}, [])
useEffect(() => {
if (autoRun) {
if (props.analysisModule && state.categoryIndex.length > 0) {
compilation(props.analysisModule, state, run)
if (state.data !== null) {
run(state.data, state.source, state.file)
}
}
return () => {
return () => { }
}, [autoRun, categoryIndex, state])
const message = (name, warning, more, fileName, locationString) : string => {
return (`
<span className='d-flex flex-column'>
<span className='h6 font-weight-bold'>${name}</span>
${warning}
${more
? (<span><a href={more} target='_blank'>more</a></span>)
: (<span> </span>)
}
}, [autoRun, state, props.analysisModule, setWarning])
<span className="" title={Position in ${fileName}}>Pos: ${locationString}</span>
</span>`
)
}
const run = (lastCompilationResult, lastCompilationSource, currentFile) => {
if (autoRun) {
setResult({ lastCompilationResult, lastCompilationSource, currentFile })
if (lastCompilationResult && state.categoryIndex.length > 0) {
setRunButtonState(false)
if (lastCompilationResult && categoryIndex.length > 0) {
let warningCount = 0
const warningMessage = []
runner.run(lastCompilationResult, state.categoryIndex, results => {
runner.run(lastCompilationResult, categoryIndex, results => {
results.map((result) => {
let moduleName
Object.keys(groupedModules).map(key => {
@ -156,7 +104,6 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
}
})
})
setModuleNameResult(moduleName)
const warningErrors = []
result.report.map((item) => {
let location: any = {}
@ -183,6 +130,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
fileName = Object.keys(lastCompilationResult.contracts)[file]
}
warningCount++
const msg = message(item.name, item.warning, item.more, fileName, locationString)
const options = {
type: 'warning',
useSpan: true,
@ -197,8 +145,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
location: location
}
warningErrors.push(options)
setWarning({ hasWarning: true, options, warningErrors: warningErrors })
warningMessage.push({ options, hasWarning: true, warningModuleName: moduleName })
warningMessage.push({ msg, options, hasWarning: true, warningModuleName: moduleName })
})
})
const resultArray = []
@ -216,21 +163,15 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
return acc
}, {})
}
const groupedCategory = groupBy(resultArray, 'warningModuleName')
console.log({ warningCount }, ' 221')
console.log({ groupedCategory })
setWarningState(groupedCategory)
console.log({ warningState })
console.log({ warningCount }, ' 223')
})
console.log({ warningCount }, ' CategoryIndex outside function')
if (state.categoryIndex.length > 0) {
console.log(state.categoryIndex, ' CategoryIndex in execute funtions')
if (categoryIndex.length > 0) {
props.event.trigger('staticAnaysisWarning', [warningCount])
}
} else {
setRunButtonState(true)
if (state.categoryIndex.length) {
if (categoryIndex.length) {
warningContainer.current.innerText = 'No compiled AST available'
}
props.event.trigger('staticAnaysisWarning', [-1])
@ -240,19 +181,27 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
const handleCheckAllModules = (groupedModules) => {
const index = groupedModuleIndex(groupedModules)
if (index.every(el => state.categoryIndex.includes(el))) {
dispatch({ type: 'uncheck', payload: index })
if (index.every(el => categoryIndex.includes(el))) {
setCategoryIndex(
categoryIndex.filter((el) => {
return !index.includes(el)
})
)
} else {
dispatch({ type: 'check', payload: index })
setCategoryIndex(_.uniq([...categoryIndex, ...index]))
}
}
const handleCheckOrUncheckCategory = (category) => {
const index = groupedModuleIndex(category)
if (index.every(el => state.categoryIndex.includes(el))) {
dispatch({ type: 'uncheck', payload: index })
if (index.every(el => categoryIndex.includes(el))) {
setCategoryIndex(
categoryIndex.filter((el) => {
return !index.includes(el)
})
)
} else {
dispatch({ type: 'check', payload: index })
setCategoryIndex(_.uniq([...categoryIndex, ...index]))
}
}
@ -266,10 +215,10 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
const handleCheckSingle = (event, _index) => {
_index = _index.toString()
if (state.categoryIndex.includes(_index)) {
dispatch({ type: 'uncheckSingle', payload: _index })
if (categoryIndex.includes(_index)) {
setCategoryIndex(categoryIndex.filter(val => val !== _index))
} else {
dispatch({ type: 'checkSingle', payload: _index })
setCategoryIndex(_.uniq([...categoryIndex, _index]))
}
}
@ -284,7 +233,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
itemName={item.name}
label={item.description}
onClick={event => handleCheckSingle(event, item._index)}
checked={state.categoryIndex.includes(item._index.toString())}
checked={categoryIndex.includes(item._index.toString())}
onChange={() => {}}
/>
</div>
@ -313,7 +262,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
expand={false}
>
<div>
<RemixUiCheckbox onClick={() => handleCheckOrUncheckCategory(category)} id={categoryId} inputType="checkbox" label={`Select ${category[0].categoryDisplayName}`} name='checkCategoryEntry' checked={category.map(x => x._index.toString()).every(el => state.categoryIndex.includes(el))} onChange={() => {}}/>
<RemixUiCheckbox onClick={() => handleCheckOrUncheckCategory(category)} id={categoryId} inputType="checkbox" label={`Select ${category[0].categoryDisplayName}`} name='checkCategoryEntry' checked={category.map(x => x._index.toString()).every(el => categoryIndex.includes(el))} onChange={() => {}}/>
</div>
<div className="w-100 d-block px-2 my-1 entries collapse multi-collapse" id={`heading${categoryId}`}>
{category.map((item, i) => {
@ -340,7 +289,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
return (value.map(x => {
return x._index.toString()
}))
}).flat().every(el => state.categoryIndex.includes(el))}
}).flat().every(el => categoryIndex.includes(el))}
label="Select all"
onClick={() => handleCheckAllModules(groupedModules)}
onChange={() => {}}
@ -353,7 +302,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
label="Autorun"
onChange={() => {}}
/>
<Button buttonText="Run" onClick={() => run(result.lastCompilationResult, result.lastCompilationSource, result.currentFile)} disabled={runButtonState || state.categoryIndex.length === 0}/>
<Button buttonText="Run" onClick={() => run(state.data, state.source, state.file)} disabled={state.data === null}/>
</div>
</div>
<div id="staticanalysismodules" className="list-group list-group-flush">
@ -371,14 +320,12 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
className="text-break break-word word-break font-weight-bold"
id="staticAnalysisCurrentFile"
>
{result.currentFile && result.currentFile}
</span>
</div>
{ console.log({ warningState }) }
{ state.categoryIndex.length > 0 && Object.entries(warningState).length > 0 &&
{ categoryIndex.length > 0 && Object.entries(warningState).length > 0 &&
<div id='staticanalysisresult' >
<div className="mb-4">
{/* {
{
(Object.entries(warningState).map((element) => (
<>
<span className="text-dark h6">{element[0]}</span>
@ -392,7 +339,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
))}
</>
)))
} */}
}
</div>
</div>
}

Loading…
Cancel
Save