Skip to content

Commit 2204626

Browse files
feat: Added "dev" mode, so we can easily debug
refactored webgl for statements, to be more performant added unit tests for "for" statements will do the same for cpu for statements next fixed some issues in typings file exposed FunctionNode from standard importer
1 parent cf5a358 commit 2204626

20 files changed

+20950
-610
lines changed

bin/gpu-browser-core.js

Lines changed: 415 additions & 192 deletions
Large diffs are not rendered by default.

bin/gpu-browser-core.min.js

Lines changed: 7237 additions & 0 deletions
Large diffs are not rendered by default.

bin/gpu-browser.js

Lines changed: 415 additions & 192 deletions
Large diffs are not rendered by default.

bin/gpu-browser.min.js

Lines changed: 12001 additions & 0 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"dependencies": {
1717
"acorn": "^5.1.1",
1818
"gl": "^4.1.1",
19+
"gpu-mock.js": "^1.0.1",
1920
"typescript": "^3.3.1"
2021
},
2122
"devDependencies": {

src/backend/cpu/function-node.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ class CPUFunctionNode extends FunctionNode {
201201
retArr.push('}\n');
202202

203203
return retArr;
204-
} else if (forNode.init.declarations) {
204+
} else if (forNode.init && forNode.init.declarations) {
205205
const declarations = forNode.init.declarations;
206206
if (!Array.isArray(declarations) || declarations.length < 1) {
207207
throw new Error('Error: Incompatible for loop declaration');
@@ -333,7 +333,14 @@ class CPUFunctionNode extends FunctionNode {
333333
const firstDeclaration = varDecNode.declarations[0];
334334
const type = this.getType(firstDeclaration.init);
335335
for (let i = 0; i < varDecNode.declarations.length; i++) {
336-
this.declarations[varDecNode.declarations[i].id.name] = type;
336+
this.declarations[varDecNode.declarations[i].id.name] = {
337+
type,
338+
dependencies: {
339+
constants: [],
340+
arguments: []
341+
},
342+
isUnsafe: false
343+
};
337344
if (i > 0) {
338345
retArr.push(',');
339346
}

src/backend/function-node.js

Lines changed: 157 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class FunctionNode {
5858

5959
this.validate();
6060
this._string = null;
61-
this._whileIndex = 0;
61+
this._internalVariableNames = {};
6262
}
6363

6464
validate() {
@@ -178,7 +178,7 @@ class FunctionNode {
178178
throw 'Missing JS to AST parser';
179179
}
180180

181-
const ast = Object.freeze(inParser.parse('const parser_' + this.name + ' = ' + this.source + ';', {
181+
const ast = Object.freeze(inParser.parse(`const parser_${ this.name } = ${ this.source };`, {
182182
locations: true
183183
}));
184184
// take out the function object, outside the var declarations
@@ -200,7 +200,7 @@ class FunctionNode {
200200
const argumentIndex = this.argumentNames.indexOf(name);
201201
if (argumentIndex === -1) {
202202
if (this.declarations[name]) {
203-
return this.declarations[name];
203+
return this.declarations[name].type;
204204
}
205205
} else {
206206
const argumentType = this.argumentTypes[argumentIndex];
@@ -218,7 +218,11 @@ class FunctionNode {
218218
}
219219
}
220220
}
221-
return type === 'Float' ? 'Number' : type;
221+
if (!type) {
222+
// TODO: strict type detection mode?
223+
// throw new Error(`Declaration of ${name} not found`);
224+
}
225+
return type;
222226
}
223227

224228
getConstantType(constantName) {
@@ -289,7 +293,12 @@ class FunctionNode {
289293
* @returns {string}
290294
*/
291295
getType(ast) {
296+
if (Array.isArray(ast)) {
297+
return this.getType(ast[ast.length - 1]);
298+
}
292299
switch (ast.type) {
300+
case 'BlockStatement':
301+
return this.getType(ast.body);
293302
case 'ArrayExpression':
294303
return `Array(${ ast.elements.length })`;
295304
case 'Literal':
@@ -307,6 +316,8 @@ class FunctionNode {
307316
// modulos is Number
308317
if (ast.operator === '%') {
309318
return 'Number';
319+
} else if (ast.operator === '>' || ast.operator === '<') {
320+
return 'Boolean';
310321
}
311322
const type = this.getType(ast.left);
312323
return typeLookupMap[type] || type;
@@ -320,11 +331,21 @@ class FunctionNode {
320331
return this.getType(ast.id);
321332
case 'Identifier':
322333
if (this.isAstVariable(ast)) {
323-
if (this.getVariableSignature(ast) === 'value') {
324-
return this.getVariableType(ast.name);
334+
const signature = this.getVariableSignature(ast);
335+
if (signature === 'value') {
336+
if (this.argumentNames.indexOf(ast.name) > -1) {
337+
return this.getVariableType(ast.name);
338+
} else if (this.declarations[ast.name]) {
339+
return this.declarations[ast.name].type;
340+
}
325341
}
326342
}
327-
throw this.astErrorOutput('Unhandled Identifier', ast);
343+
if (ast.name === 'Infinity') {
344+
return 'Integer';
345+
}
346+
return null;
347+
case 'ReturnStatement':
348+
return this.getType(ast.argument);
328349
case 'MemberExpression':
329350
if (this.isAstMathFunction(ast)) {
330351
switch (ast.property.name) {
@@ -383,13 +404,7 @@ class FunctionNode {
383404
}
384405
throw this.astErrorOutput('Unhandled getType MemberExpression', ast);
385406
case 'FunctionDeclaration':
386-
if (ast.body && ast.body.body) {
387-
const possibleReturnStatement = ast.body.body[ast.body.body.length - 1];
388-
if (possibleReturnStatement.type === 'ReturnStatement') {
389-
return this.getType(possibleReturnStatement.argument);
390-
}
391-
}
392-
throw this.astErrorOutput(`Unhandled getType Type "${ ast.type }"`, ast);
407+
return this.getType(ast.body);
393408
case 'ConditionalExpression':
394409
return this.getType(ast.consequent);
395410
default:
@@ -454,22 +469,84 @@ class FunctionNode {
454469
return ast.type === 'Identifier' || ast.type === 'MemberExpression';
455470
}
456471

457-
getFirstVariable(ast) {
472+
isSafe(ast) {
473+
return this.isSafeDependencies(this.getDependencies(ast));
474+
}
475+
476+
isSafeDependencies(dependencies) {
477+
return dependencies && dependencies.every ? dependencies.every(dependency => dependency.isSafe) : true;
478+
}
479+
480+
getDependencies(ast, dependencies, isNotSafe) {
481+
if (!dependencies) {
482+
dependencies = [];
483+
}
458484
if (!ast) return null;
485+
if (Array.isArray(ast)) {
486+
for (let i = 0; i < ast.length; i++) {
487+
this.getDependencies(ast[i], dependencies, isNotSafe);
488+
}
489+
return dependencies;
490+
}
459491
switch (ast.type) {
492+
case 'Literal':
493+
dependencies.push({
494+
origin: 'literal',
495+
value: ast.value,
496+
isSafe: isNotSafe === true ? false : ast.value > -Infinity && ast.value < Infinity && !isNaN(ast.value)
497+
});
498+
break;
499+
case 'VariableDeclarator':
500+
return this.getDependencies(ast.init, dependencies, isNotSafe);
460501
case 'Identifier':
461-
return ast;
502+
if (this.declarations[ast.name]) {
503+
dependencies.push({
504+
name: ast.name,
505+
origin: 'declaration',
506+
isSafe: isNotSafe ? false : this.isSafeDependencies(this.declarations[ast.name].dependencies),
507+
});
508+
} else if (this.argumentNames.indexOf(ast.name) > -1) {
509+
dependencies.push({
510+
name: ast.name,
511+
origin: 'argument',
512+
isSafe: false,
513+
});
514+
}
515+
break;
462516
case 'FunctionDeclaration':
463-
return this.getFirstVariable(ast.body.body[ast.body.body.length - 1]);
517+
return this.getDependencies(ast.body.body[ast.body.body.length - 1], dependencies, isNotSafe);
464518
case 'ReturnStatement':
465-
return this.getFirstVariable(ast.argument);
519+
return this.getDependencies(ast.argument, dependencies);
466520
case 'BinaryExpression':
467-
return this.getFirstVariable(ast.left);
521+
isNotSafe = (ast.operator === '/' || ast.operator === '*');
522+
this.getDependencies(ast.left, dependencies, isNotSafe);
523+
this.getDependencies(ast.right, dependencies, isNotSafe);
524+
return dependencies;
468525
case 'UpdateExpression':
469-
return this.getFirstVariable(ast.argument);
526+
return this.getDependencies(ast.argument, dependencies, isNotSafe);
527+
case 'VariableDeclaration':
528+
return this.getDependencies(ast.declarations, dependencies, isNotSafe);
529+
case 'ArrayExpression':
530+
dependencies.push({
531+
origin: 'declaration',
532+
isSafe: true,
533+
});
534+
return dependencies;
535+
case 'CallExpression':
536+
dependencies.push({
537+
origin: 'function',
538+
isSafe: true,
539+
});
540+
return dependencies;
541+
case 'MemberExpression':
542+
const details = this.getMemberExpressionDetails(ast);
543+
if (details) {
544+
return details.type;
545+
}
470546
default:
471-
throw this.astErrorOutput('Unhandled variable lookup', ast);
547+
throw this.astErrorOutput(`Unhandled type ${ ast.type } in getAllVariables`, ast);
472548
}
549+
return dependencies;
473550
}
474551

475552
getVariableSignature(ast) {
@@ -719,7 +796,7 @@ class FunctionNode {
719796
* @returns {Array} the append retArr
720797
*/
721798
astBreakStatement(brNode, retArr) {
722-
retArr.push('break;\n');
799+
retArr.push('break;');
723800
return retArr;
724801
}
725802
/**
@@ -741,7 +818,53 @@ class FunctionNode {
741818
astDoWhileStatement(ast, retArr) {
742819
return retArr;
743820
}
744-
astVariableDeclaration(ast, retArr) {
821+
/**
822+
* @desc Parses the abstract syntax tree for *Variable Declaration*
823+
* @param {Object} varDecNode - An ast Node
824+
* @param {Array} retArr - return array string
825+
* @returns {Array} the append retArr
826+
*/
827+
astVariableDeclaration(varDecNode, retArr) {
828+
const declarations = varDecNode.declarations;
829+
if (!declarations || !declarations[0] || !declarations[0].init) {
830+
throw this.astErrorOutput('Unexpected expression', varDecNode);
831+
}
832+
const result = [];
833+
const firstDeclaration = declarations[0];
834+
const init = firstDeclaration.init;
835+
let type = this.isState('in-for-loop-init') ? 'Integer' : this.getType(init);
836+
if (type === 'LiteralInteger') {
837+
// We had the choice to go either float or int, choosing float
838+
type = 'Number';
839+
}
840+
const markupType = typeMap[type];
841+
if (!markupType) {
842+
throw this.astErrorOutput(`Markup type ${ markupType } not handled`, varDecNode);
843+
}
844+
let dependencies = this.getDependencies(firstDeclaration.init);
845+
this.declarations[firstDeclaration.id.name] = Object.freeze({
846+
type,
847+
dependencies,
848+
isSafe: dependencies.every(dependency => dependency.isSafe)
849+
});
850+
const initResult = [`${type} user_${firstDeclaration.id.name}=`];
851+
this.astGeneric(init, initResult);
852+
result.push(initResult.join(''));
853+
854+
// first declaration is done, now any added ones setup
855+
for (let i = 1; i < declarations.length; i++) {
856+
const declaration = declarations[i];
857+
dependencies = this.getDependencies(declaration);
858+
this.declarations[declaration.id.name] = Object.freeze({
859+
type,
860+
dependencies,
861+
isSafe: false
862+
});
863+
this.astGeneric(declaration, result);
864+
}
865+
866+
retArr.push(retArr, result.join(','));
867+
retArr.push(';');
745868
return retArr;
746869
}
747870
/**
@@ -992,6 +1115,17 @@ class FunctionNode {
9921115
throw this.astErrorOutput('Unexpected expression', ast);
9931116
}
9941117
}
1118+
1119+
getInternalVariableName(name) {
1120+
if (!this._internalVariableNames.hasOwnProperty(name)) {
1121+
this._internalVariableNames[name] = 0;
1122+
}
1123+
this._internalVariableNames[name]++;
1124+
if (this._internalVariableNames[name] === 1) {
1125+
return name;
1126+
}
1127+
return name + this._internalVariableNames[name];
1128+
}
9951129
}
9961130

9971131
const typeLookupMap = {

src/backend/kernel.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class Kernel {
109109

110110
/**
111111
*
112-
* @type {INativeFunction[]}
112+
* @type {INativeFunctionList[]}
113113
*/
114114
this.nativeFunctions = null;
115115

src/backend/web-gl/fragment-shader.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ float get(sampler2D tex, ivec2 texSize, ivec3 texDim, int bitRatio, int z, int y
122122
__GET_TEXTURE_INDEX__;
123123
vec4 texel = texture2D(tex, st / vec2(texSize));
124124
__GET_RESULT__;
125-
126125
}
127126
128127
vec4 getImage2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {

0 commit comments

Comments
 (0)