Skip to content

Commit c4f6a1b

Browse files
Merge pull request #528 from gpujs/524-coverage-compatibility
524 coverage compatibility
2 parents a1b80b7 + ade8885 commit c4f6a1b

16 files changed

+326
-13
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,26 @@ Settings are an object used to create an instance of `GPU`. Example: `new GPU(s
154154
* 'webgl2': Use the `WebGL2Kernel` for transpiling a kernel
155155
* 'headlessgl' **New in V2!**: Use the `HeadlessGLKernel` for transpiling a kernel
156156
* 'cpu': Use the `CPUKernel` for transpiling a kernel
157+
* `onIstanbulCoverageVariable`: For testing. Used for when coverage is inject into function values, and is desired to be preserved (`cpu` mode ONLY).
158+
Use like this:
159+
```
160+
const { getFileCoverageDataByName } = require('istanbul-spy');
161+
const gpu = new GPU({
162+
mode: 'cpu',
163+
onIstanbulCoverageVariable: (name, kernel) => {
164+
const data = getFileCoverageDataByName(name);
165+
if (!data) {
166+
throw new Error(`Could not find istanbul identifier ${name}`);
167+
}
168+
const { path } = getFileCoverageDataByName(name);
169+
const variable = `const ${name} = __coverage__['${path}'];\n`;
170+
if (!kernel.hasPrependString(variable)) {
171+
kernel.prependString(variable);
172+
}
173+
}
174+
});
175+
```
176+
* `removeIstanbulCoverage`: Boolean. For testing and code coverage. Removes istanbul artifacts that were injected at testing runtime.
157177

158178
## `gpu.createKernel` Settings
159179
Settings are an object used to create a `kernel` or `kernelMap`. Example: `gpu.createKernel(settings)`

src/backend/cpu/function-node.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,18 @@ class CPUFunctionNode extends FunctionNode {
466466
return retArr;
467467
}
468468
break;
469+
case 'value.value[]': // istanbul coverage variable
470+
if (this.removeIstanbulCoverage) {
471+
return retArr;
472+
}
473+
retArr.push(`${mNode.object.object.name}.${mNode.object.property.name}[${mNode.property.value}]`);
474+
return retArr;
475+
case 'value.value[][]': // istanbul coverage variable
476+
if (this.removeIstanbulCoverage) {
477+
return retArr;
478+
}
479+
retArr.push(`${mNode.object.object.object.name}.${mNode.object.object.property.name}[${mNode.object.property.value}][${mNode.property.value}]`);
480+
return retArr;
469481
case 'this.constants.value':
470482
case 'this.constants.value[]':
471483
case 'this.constants.value[][]':

src/backend/cpu/kernel.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class CPUKernel extends Kernel {
5050
this._imageData = null;
5151
this._colorData = null;
5252
this._kernelString = null;
53+
this._prependedString = [];
5354
this.thread = {
5455
x: 0,
5556
y: 0,
@@ -213,7 +214,7 @@ class CPUKernel extends Kernel {
213214
if (/^function/.test(fn)) return fn;
214215
kernelThreadString = fn;
215216
return false;
216-
})
217+
});
217218
} else {
218219
kernelThreadString = translatedSources.shift();
219220
}
@@ -222,6 +223,7 @@ class CPUKernel extends Kernel {
222223
const _this = this;
223224
${ this._processConstants() }
224225
return (${ this.argumentNames.map(argumentName => 'user_' + argumentName).join(', ') }) => {
226+
${ this._prependedString.join('') }
225227
${ this._processArguments() }
226228
${ this.graphical ? this._graphicalKernelBody(kernelThreadString) : this._resultKernelBody(kernelThreadString) }
227229
${ translatedSources.length > 0 ? translatedSources.join('\n') : '' }
@@ -520,6 +522,15 @@ class CPUKernel extends Kernel {
520522
this._colorData = new Uint8ClampedArray(width * height * 4);
521523
}
522524
}
525+
526+
prependString(value) {
527+
if (this._kernelString) throw new Error('Kernel already built');
528+
this._prependedString.push(value);
529+
}
530+
531+
hasPrependString(value) {
532+
return this._prependedString.indexOf(value) > -1;
533+
}
523534
}
524535

525536
module.exports = {

src/backend/function-builder.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class FunctionBuilder {
3636
dynamicArguments,
3737
dynamicOutput,
3838
warnVarUsage,
39+
onIstanbulCoverageVariable,
40+
removeIstanbulCoverage,
3941
} = kernel;
4042

4143
const argumentTypes = new Array(kernelArguments.length);
@@ -123,6 +125,9 @@ class FunctionBuilder {
123125
triggerImplyArgumentType,
124126
triggerImplyArgumentBitRatio,
125127
onFunctionCall,
128+
warnVarUsage,
129+
onIstanbulCoverageVariable: onIstanbulCoverageVariable ? (name) => onIstanbulCoverageVariable(name, kernel) : null,
130+
removeIstanbulCoverage,
126131
optimizeFloatMemory,
127132
precision,
128133
constants,
@@ -175,6 +180,8 @@ class FunctionBuilder {
175180
triggerImplyArgumentBitRatio,
176181
onFunctionCall,
177182
onNestedFunction,
183+
onIstanbulCoverageVariable: onIstanbulCoverageVariable ? (name) => onIstanbulCoverageVariable(name, kernel) : null,
184+
removeIstanbulCoverage,
178185
}));
179186
}
180187

src/backend/function-node.js

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ class FunctionNode {
6262
this.strictTypingChecking = false;
6363
this.fixIntegerDivisionAccuracy = null;
6464
this.warnVarUsage = true;
65+
this.onIstanbulCoverageVariable = null;
66+
this.removeIstanbulCoverage = false;
6567

6668
if (settings) {
6769
for (const p in settings) {
@@ -571,6 +573,8 @@ class FunctionNode {
571573
return null;
572574
case 'IfStatement':
573575
return this.getType(ast.consequent);
576+
case 'SequenceExpression':
577+
return this.getType(ast.expressions[ast.expressions.length - 1]);
574578
default:
575579
throw this.astErrorOutput(`Unhandled getType Type "${ ast.type }"`, ast);
576580
}
@@ -771,6 +775,8 @@ class FunctionNode {
771775
}
772776
return dependencies;
773777
}
778+
case 'SequenceExpression':
779+
return this.getDependencies(ast.expressions, dependencies, isNotSafe);
774780
default:
775781
throw this.astErrorOutput(`Unhandled type ${ ast.type } in getDependencies`, ast);
776782
}
@@ -827,6 +833,8 @@ class FunctionNode {
827833
'value[][][]',
828834
'value[][][][]',
829835
'value.value',
836+
'value.value[]', // istanbul coverage
837+
'value.value[][]', // istanbul coverage
830838
'value.thread.value',
831839
'this.thread.value',
832840
'this.output.value',
@@ -1144,12 +1152,28 @@ class FunctionNode {
11441152
astThisExpression(ast, retArr) {
11451153
return retArr;
11461154
}
1155+
isIstanbulAST(ast) {
1156+
const variableSignature = this.getVariableSignature(ast);
1157+
return variableSignature === 'value.value[]' || variableSignature === 'value.value[][]';
1158+
}
11471159
astSequenceExpression(sNode, retArr) {
1148-
for (let i = 0; i < sNode.expressions.length; i++) {
1149-
if (i > 0) {
1150-
retArr.push(',');
1160+
const { expressions } = sNode;
1161+
const sequenceResult = [];
1162+
for (let i = 0; i < expressions.length; i++) {
1163+
const expression = expressions[i];
1164+
if (this.removeIstanbulCoverage) {
1165+
if (expression.type === 'UpdateExpression' && this.isIstanbulAST(expression.argument)) {
1166+
continue;
1167+
}
11511168
}
1152-
this.astGeneric(sNode.expressions, retArr);
1169+
const expressionResult = [];
1170+
this.astGeneric(expression, expressionResult);
1171+
sequenceResult.push(expressionResult.join(''));
1172+
}
1173+
if (sequenceResult.length > 1) {
1174+
retArr.push('(', sequenceResult.join(','), ')');
1175+
} else {
1176+
retArr.push(sequenceResult[0]);
11531177
}
11541178
return retArr;
11551179
}
@@ -1185,6 +1209,12 @@ class FunctionNode {
11851209
* @returns {Array} the append retArr
11861210
*/
11871211
astUpdateExpression(uNode, retArr) {
1212+
if (this.removeIstanbulCoverage) {
1213+
const signature = this.getVariableSignature(uNode.argument);
1214+
if (this.isIstanbulAST(uNode.argument)) {
1215+
return retArr;
1216+
}
1217+
}
11881218
if (uNode.prefix) {
11891219
retArr.push(uNode.operator);
11901220
this.astGeneric(uNode.argument, retArr);
@@ -1398,8 +1428,28 @@ class FunctionNode {
13981428
signature: variableSignature,
13991429
property: ast.property,
14001430
};
1401-
default:
1402-
throw this.astErrorOutput('Unexpected expression', ast);
1431+
case 'value.value[]': // istanbul coverage
1432+
if (this.removeIstanbulCoverage) {
1433+
return { signature: variableSignature };
1434+
}
1435+
if (this.onIstanbulCoverageVariable) {
1436+
this.onIstanbulCoverageVariable(ast.object.object.name);
1437+
return {
1438+
signature: variableSignature
1439+
};
1440+
}
1441+
case 'value.value[][]': // istanbul coverage
1442+
if (this.removeIstanbulCoverage) {
1443+
return { signature: variableSignature };
1444+
}
1445+
if (this.onIstanbulCoverageVariable) {
1446+
this.onIstanbulCoverageVariable(ast.object.object.object.name);
1447+
return {
1448+
signature: variableSignature
1449+
};
1450+
}
1451+
default:
1452+
throw this.astErrorOutput('Unexpected expression', ast);
14031453
}
14041454
}
14051455

src/backend/function-tracer.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ class FunctionTracer {
124124
case 'ExpressionStatement':
125125
this.scan(ast.expression);
126126
break;
127+
case 'SequenceExpression':
128+
this.scan(ast.expressions);
129+
break;
127130
case 'CallExpression':
128131
this.functionCalls.push({
129132
context: this.currentContext,
@@ -155,7 +158,6 @@ class FunctionTracer {
155158
case 'BreakStatement':
156159
case 'ContinueStatement':
157160
break;
158-
159161
default:
160162
throw new Error(`unhandled type "${ast.type}"`);
161163
}

src/backend/kernel.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ class Kernel {
200200
this.strictIntegers = false;
201201
this.fixIntegerDivisionAccuracy = null;
202202
this.warnVarUsage = true;
203+
this.onIstanbulCoverageVariable = null;
204+
this.removeIstanbulCoverage = false;
203205
}
204206

205207
/**
@@ -724,6 +726,23 @@ class Kernel {
724726
}
725727
}
726728

729+
/**
730+
*
731+
* @param {String} value
732+
*/
733+
prependString(value) {
734+
throw new Error(`"prependString" called on ${ this.constructor.name }`);
735+
}
736+
737+
/**
738+
*
739+
* @param {String} value
740+
* @return Boolean
741+
*/
742+
hasPrependString(value) {
743+
throw new Error(`"hasPrependString" called on ${ this.constructor.name }`);
744+
}
745+
727746
/**
728747
* @return {IJSON}
729748
*/

src/backend/web-gl/function-node.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,8 +1144,13 @@ class WebGLFunctionNode extends FunctionNode {
11441144
retArr.push(this.memberExpressionPropertyMarkup(property));
11451145
retArr.push(']');
11461146
return retArr;
1147-
default:
1148-
throw this.astErrorOutput('Unexpected expression', mNode);
1147+
case 'value.value[]':
1148+
case 'value.value[][]':
1149+
if (this.removeIstanbulCoverage) {
1150+
return retArr;
1151+
}
1152+
default:
1153+
throw this.astErrorOutput('Unexpected expression', mNode);
11491154
}
11501155

11511156
if (mNode.computed === false) {

src/backend/web-gl/kernel.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ class WebGLKernel extends GLKernel {
153153
this.maxTexSize = null;
154154
this.switchingKernels = false;
155155
this.onRequestSwitchKernel = null;
156+
this.removeIstanbulCoverage = true;
156157

157158
this.mergeSettings(source.settings || settings);
158159

src/gpu.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ class GPU {
116116
this.functions = [];
117117
this.nativeFunctions = [];
118118
this.injectedNative = null;
119+
this.onIstanbulCoverageVariable = settings.onIstanbulCoverageVariable || null;
120+
this.removeIstanbulCoverage = settings.removeIstanbulCoverage || false;
119121
if (this.mode === 'dev') return;
120122
this.chooseKernel();
121123
// add functions from settings
@@ -306,6 +308,8 @@ class GPU {
306308
validate,
307309
warnVarUsage: kernel.warnVarUsage,
308310
returnType: kernel.returnType,
311+
onIstanbulCoverageVariable: kernel.onIstanbulCoverageVariable,
312+
removeIstanbulCoverage: kernel.removeIstanbulCoverage,
309313
onRequestFallback,
310314
onRequestSwitchKernel,
311315
});
@@ -324,6 +328,8 @@ class GPU {
324328
functions: this.functions,
325329
nativeFunctions: this.nativeFunctions,
326330
injectedNative: this.injectedNative,
331+
onIstanbulCoverageVariable: this.onIstanbulCoverageVariable,
332+
removeIstanbulCoverage: this.removeIstanbulCoverage,
327333
gpu: this,
328334
validate,
329335
onRequestFallback,

0 commit comments

Comments
 (0)