Skip to content

Commit 1e60084

Browse files
feat: Full type inference (this is a HUGE feature)
feat: Add mandelbulb.html example fix: Bump and build
1 parent 9540340 commit 1e60084

24 files changed

+1405
-182
lines changed

bin/gpu-browser-core.js

Lines changed: 121 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
*
55
* GPU Accelerated JavaScript
66
*
7-
* @version 2.0.0-rc.5
8-
* @date Tue Mar 19 2019 08:40:39 GMT-0400 (Eastern Daylight Time)
7+
* @version 2.0.0-rc.6
8+
* @date Tue Mar 19 2019 19:50:06 GMT-0400 (Eastern Daylight Time)
99
*
1010
* @license MIT
1111
* The MIT License
@@ -1179,18 +1179,19 @@ class FunctionBuilder {
11791179

11801180
const onNestedFunction = (fnString, returnType) => {
11811181
functionBuilder.addFunctionNode(new FunctionNode(fnString, Object.assign({}, nodeOptions, {
1182-
returnType
1182+
returnType: returnType || 'Number',
1183+
lookupReturnType
11831184
})));
11841185
};
11851186

11861187
const parsedReturnTypes = {};
1187-
const lookupReturnType = (functionName) => {
1188+
const lookupReturnType = (functionName, ast, requestingNode) => {
11881189
if (parsedReturnTypes[functionName]) return parsedReturnTypes[functionName];
11891190
const source = functionBuilder.nativeFunctions[functionName];
11901191
if (source) {
11911192
return parsedReturnTypes[functionName] = kernel.constructor.nativeFunctionReturnType(source);
11921193
}
1193-
return functionBuilder.lookupReturnType(functionName);
1194+
return functionBuilder.lookupReturnType(functionName, ast, requestingNode);
11941195
};
11951196

11961197
const nativeFunctionReturnTypes = {};
@@ -1241,6 +1242,7 @@ class FunctionBuilder {
12411242
plugins,
12421243
constants,
12431244
constantTypes,
1245+
lookupReturnType,
12441246
}));
12451247
}
12461248

@@ -1254,7 +1256,9 @@ class FunctionBuilder {
12541256
return new FunctionNode(source, Object.assign({}, nodeOptions, {
12551257
name,
12561258
isSubKernel: true,
1257-
isRootKernel: false
1259+
isRootKernel: false,
1260+
returnType: 'Number',
1261+
lookupReturnType,
12581262
}));
12591263
});
12601264
}
@@ -1414,11 +1418,16 @@ class FunctionBuilder {
14141418
return this.getStringFromFunctionNames(Object.keys(this.functionMap));
14151419
}
14161420

1417-
lookupReturnType(functionName) {
1421+
lookupReturnType(functionName, ast, requestingNode) {
14181422
const node = this.functionMap[functionName];
1419-
if (node && node.returnType) {
1420-
return node.returnType;
1423+
if (node) {
1424+
if (node.returnType) {
1425+
return node.returnType;
1426+
} else {
1427+
return node.getType(node.getJsAST());
1428+
}
14211429
}
1430+
14221431
return null;
14231432
}
14241433
}
@@ -1474,7 +1483,7 @@ class FunctionNode {
14741483
}
14751484
}
14761485

1477-
if (!this.returnType) {
1486+
if (this.isRootKernel && !this.returnType) {
14781487
this.returnType = 'Number';
14791488
}
14801489

@@ -1691,16 +1700,26 @@ class FunctionNode {
16911700
if (this.isAstMathFunction(ast)) {
16921701
return 'Number';
16931702
}
1694-
if (!ast.callee || !ast.callee.name) return null;
1703+
if (!ast.callee || !ast.callee.name) {
1704+
if (ast.callee.type === 'SequenceExpression') {
1705+
return this.getType(ast.callee.expressions[ast.callee.expressions.length - 1]);
1706+
}
1707+
throw this.astErrorOutput('Unknown call expression', ast);
1708+
}
16951709
if (this.nativeFunctionReturnTypes && this.nativeFunctionReturnTypes[ast.callee.name]) {
16961710
return this.nativeFunctionReturnTypes[ast.callee.name];
16971711
}
1698-
return ast.callee && ast.callee.name && this.lookupReturnType ? this.lookupReturnType(ast.callee.name) : null;
1712+
if (ast.callee && ast.callee.name && this.lookupReturnType) {
1713+
return this.lookupReturnType(ast.callee.name, ast, this);
1714+
}
1715+
throw this.astErrorOutput(`Unhandled getType Type "${ ast.type }"`, ast);
16991716
case 'BinaryExpression':
1700-
if (ast.operator === '%') {
1701-
return 'Number';
1702-
} else if (ast.operator === '>' || ast.operator === '<') {
1703-
return 'Boolean';
1717+
switch (ast.operator) {
1718+
case '%':
1719+
return 'Number';
1720+
case '>':
1721+
case '<':
1722+
return 'Boolean';
17041723
}
17051724
const type = this.getType(ast.left);
17061725
return typeLookupMap[type] || type;
@@ -1726,6 +1745,10 @@ class FunctionNode {
17261745
if (ast.name === 'Infinity') {
17271746
return 'Number';
17281747
}
1748+
const origin = this.findIdentifierOrigin(ast);
1749+
if (origin && origin.init) {
1750+
return this.getType(origin.init);
1751+
}
17291752
return null;
17301753
case 'ReturnStatement':
17311754
return this.getType(ast.argument);
@@ -1782,6 +1805,9 @@ class FunctionNode {
17821805
case 'a':
17831806
return typeLookupMap[this.getVariableType(ast.object.name)];
17841807
}
1808+
if (ast.object.name === '_' + ast.property.name && this.lookupReturnType) {
1809+
return this.lookupReturnType(ast.property.name, ast, this);
1810+
}
17851811
}
17861812
throw this.astErrorOutput('Unhandled getType MemberExpression', ast);
17871813
}
@@ -1790,6 +1816,8 @@ class FunctionNode {
17901816
return this.getType(ast.body);
17911817
case 'ConditionalExpression':
17921818
return this.getType(ast.consequent);
1819+
case 'FunctionExpression':
1820+
return this.getType(ast.body);
17931821
default:
17941822
throw this.astErrorOutput(`Unhandled getType Type "${ ast.type }"`, ast);
17951823
}
@@ -2428,6 +2456,53 @@ class FunctionNode {
24282456
}
24292457
}
24302458

2459+
findIdentifierOrigin(astToFind) {
2460+
const stack = [this.ast];
2461+
2462+
while (stack.length > 0) {
2463+
const atNode = stack[0];
2464+
if (atNode.type === 'VariableDeclarator' && atNode.id && atNode.id.name && atNode.id.name === astToFind.name) {
2465+
return atNode;
2466+
}
2467+
stack.shift();
2468+
if (atNode.argument) {
2469+
stack.push(atNode.argument);
2470+
} else if (atNode.body) {
2471+
stack.push(atNode.body);
2472+
} else if (atNode.declarations) {
2473+
stack.push(atNode.declarations);
2474+
} else if (Array.isArray(atNode)) {
2475+
for (let i = 0; i < atNode.length; i++) {
2476+
stack.push(atNode[i]);
2477+
}
2478+
}
2479+
}
2480+
return null;
2481+
}
2482+
2483+
findLastReturn() {
2484+
const stack = [this.ast];
2485+
2486+
while (stack.length > 0) {
2487+
const atNode = stack.pop();
2488+
if (atNode.type === 'ReturnStatement') {
2489+
return atNode;
2490+
}
2491+
if (atNode.argument) {
2492+
stack.push(atNode.argument);
2493+
} else if (atNode.body) {
2494+
stack.push(atNode.body);
2495+
} else if (atNode.declarations) {
2496+
stack.push(atNode.declarations);
2497+
} else if (Array.isArray(atNode)) {
2498+
for (let i = 0; i < atNode.length; i++) {
2499+
stack.push(atNode[i]);
2500+
}
2501+
}
2502+
}
2503+
return null;
2504+
}
2505+
24312506
getInternalVariableName(name) {
24322507
if (!this._internalVariableNames.hasOwnProperty(name)) {
24332508
this._internalVariableNames[name] = 0;
@@ -3226,18 +3301,31 @@ class WebGLFunctionNode extends FunctionNode {
32263301
}
32273302

32283303
astFunctionExpression(ast, retArr) {
3229-
32303304
if (this.isRootKernel) {
32313305
retArr.push('void');
32323306
} else {
3307+
if (!this.returnType) {
3308+
const lastReturn = this.findLastReturn();
3309+
if (lastReturn) {
3310+
this.returnType = this.getType(ast.body);
3311+
if (this.returnType === 'LiteralInteger') {
3312+
this.returnType = 'Number';
3313+
}
3314+
}
3315+
}
3316+
32333317
const {
32343318
returnType
32353319
} = this;
3236-
const type = typeMap[returnType];
3237-
if (!type) {
3238-
throw new Error(`unknown type ${ returnType }`);
3320+
if (!returnType) {
3321+
retArr.push('void');
3322+
} else {
3323+
const type = typeMap[returnType];
3324+
if (!type) {
3325+
throw new Error(`unknown type ${returnType}`);
3326+
}
3327+
retArr.push(type);
32393328
}
3240-
retArr.push(type);
32413329
}
32423330
retArr.push(' ');
32433331
retArr.push(this.name);
@@ -3282,7 +3370,16 @@ class WebGLFunctionNode extends FunctionNode {
32823370

32833371
const result = [];
32843372

3373+
if (!this.returnType) {
3374+
if (this.isRootKernel) {
3375+
this.returnType = 'Number';
3376+
} else {
3377+
this.returnType = type;
3378+
}
3379+
}
3380+
32853381
switch (this.returnType) {
3382+
case 'LiteralInteger':
32863383
case 'Number':
32873384
case 'Float':
32883385
switch (type) {
@@ -3345,7 +3442,6 @@ class WebGLFunctionNode extends FunctionNode {
33453442
}
33463443

33473444
astLiteral(ast, retArr) {
3348-
33493445
if (isNaN(ast.value)) {
33503446
throw this.astErrorOutput(
33513447
'Non-numeric literal not supported : ' + ast.value,
@@ -5342,9 +5438,9 @@ class WebGLKernel extends GLKernel {
53425438
formatArrayTransfer(value, length) {
53435439
let bitRatio = 1;
53445440
let valuesFlat = value;
5345-
if (this.floatTextures) {
5346-
length *= 4;
5347-
}
5441+
if (this.floatTextures) {
5442+
length *= 4;
5443+
}
53485444
if (utils.isArray(value[0]) || this.floatTextures) {
53495445
valuesFlat = new Float32Array(length);
53505446
utils.flattenTo(value, valuesFlat);
@@ -5744,7 +5840,6 @@ class WebGLKernel extends GLKernel {
57445840
module.exports = {
57455841
WebGLKernel
57465842
};
5747-
57485843
},{"../../plugins/triangle-noise":26,"../../texture":27,"../../utils":28,"../function-builder":7,"../gl-kernel":9,"./fragment-shader":12,"./function-node":13,"./kernel-string":14,"./vertex-shader":16}],16:[function(require,module,exports){
57495844
const vertexShader = `precision highp float;
57505845
precision highp int;
@@ -6885,7 +6980,6 @@ class WebGL2Kernel extends WebGLKernel {
68856980
module.exports = {
68866981
WebGL2Kernel
68876982
};
6888-
68896983
},{"../../texture":27,"../../utils":28,"../function-builder":7,"../web-gl/kernel":15,"./fragment-shader":17,"./function-node":18,"./vertex-shader":20}],20:[function(require,module,exports){
68906984
const vertexShader = `#version 300 es
68916985
precision highp float;

0 commit comments

Comments
 (0)