diff --git a/bin/gpu-browser-core.js b/bin/gpu-browser-core.js index 6b7de894..3a212a56 100644 --- a/bin/gpu-browser-core.js +++ b/bin/gpu-browser-core.js @@ -4,8 +4,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.16 - * @date Thu Jun 20 2019 10:09:00 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.17 + * @date Mon Jun 24 2019 21:10:10 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -1677,12 +1677,16 @@ class FunctionBuilder { dynamicOutput, } = kernel; - const lookupReturnType = (functionName, ast, requestingNode) => { - return functionBuilder.lookupReturnType(functionName, ast, requestingNode); + const needsArgumentType = (functionName, index) => { + return functionBuilder.needsArgumentType(functionName, index); }; - const lookupArgumentType = (argumentName, requestingNode) => { - return functionBuilder.lookupArgumentType(argumentName, requestingNode); + const assignArgumentType = (functionName, index, type) => { + functionBuilder.assignArgumentType(functionName, index, type); + }; + + const lookupReturnType = (functionName, ast, requestingNode) => { + return functionBuilder.lookupReturnType(functionName, ast, requestingNode); }; const lookupFunctionArgumentTypes = (functionName) => { @@ -1724,10 +1728,11 @@ class FunctionBuilder { name: ast.id.name, argumentNames, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -1741,10 +1746,11 @@ class FunctionBuilder { isRootKernel: false, onNestedFunction, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -1791,10 +1797,11 @@ class FunctionBuilder { optimizeFloatMemory, precision, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -1974,37 +1981,6 @@ class FunctionBuilder { return this.getStringFromFunctionNames(Object.keys(this.functionMap)); } - lookupArgumentType(argumentName, requestingNode) { - const index = requestingNode.argumentNames.indexOf(argumentName); - if (index === -1) { - return null; - } - if (this.lookupChain.length === 0) { - return null; - } - let link = this.lookupChain[this.lookupChain.length - 1 - this.argumentChain.length]; - if (!link) { - return null; - } - const { - ast, - requestingNode: parentRequestingNode - } = link; - if (ast.arguments.length === 0) { - return null; - } - const usedArgument = ast.arguments[index]; - if (!usedArgument) { - return null; - } - - this.argumentChain.push(argumentName); - - const type = parentRequestingNode.getType(usedArgument); - this.argumentChain.pop(); - return type; - } - lookupReturnType(functionName, ast, requestingNode) { if (ast.type !== 'CallExpression') { throw new Error(`expected ast type of "CallExpression", but is ${ ast.type }`); @@ -2047,6 +2023,7 @@ class FunctionBuilder { } return null; + } _getFunction(functionName) { @@ -2113,6 +2090,12 @@ class FunctionBuilder { } } + needsArgumentType(functionName, i) { + if (!this._isFunction(functionName)) return false; + const fnNode = this._getFunction(functionName); + return !fnNode.argumentTypes[i]; + } + assignArgumentType(functionName, i, argumentType, requestingNode) { if (!this._isFunction(functionName)) return; const fnNode = this._getFunction(functionName); @@ -2228,8 +2211,9 @@ class FunctionNode { this.contexts = null; this.functionCalls = null; this.states = []; + this.needsArgumentType = null; + this.assignArgumentType = null; this.lookupReturnType = null; - this.lookupArgumentType = null; this.lookupFunctionArgumentTypes = null; this.lookupFunctionArgumentBitRatio = null; this.triggerImplyArgumentType = null; @@ -2462,12 +2446,6 @@ class FunctionNode { const argumentType = this.argumentTypes[argumentIndex]; if (argumentType) { type = argumentType; - } else if (this.lookupArgumentType) { - const foundArgumentType = this.lookupArgumentType(ast.name, this); - if (!this.argumentTypes[argumentIndex]) { - this.argumentTypes[argumentIndex] = foundArgumentType; - } - type = foundArgumentType; } } if (!type && this.strictTypingChecking) { @@ -2561,12 +2539,16 @@ class FunctionNode { } if (!ast.callee || !ast.callee.name) { if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[ast.callee.expressions.length - 1].property.name) { - return this.lookupReturnType(ast.callee.expressions[ast.callee.expressions.length - 1].property.name, ast, this); + const functionName = ast.callee.expressions[ast.callee.expressions.length - 1].property.name; + this.inferArgumentTypesIfNeeded(functionName, ast.arguments); + return this.lookupReturnType(functionName, ast, this); } throw this.astErrorOutput('Unknown call expression', ast); } if (ast.callee && ast.callee.name) { - return this.lookupReturnType(ast.callee.name, ast, this); + const functionName = ast.callee.name; + this.inferArgumentTypesIfNeeded(functionName, ast.arguments); + return this.lookupReturnType(functionName, ast, this); } throw this.astErrorOutput(`Unhandled getType Type "${ ast.type }"`, ast); case 'BinaryExpression': @@ -2717,6 +2699,17 @@ class FunctionNode { } } + inferArgumentTypesIfNeeded(functionName, args) { + for (let i = 0; i < args.length; i++) { + if (!this.needsArgumentType(functionName, i)) continue; + const type = this.getType(args[i]); + if (!type) { + throw this.astErrorOutput(`Unable to infer argument ${i}`, args[i]); + } + this.assignArgumentType(functionName, i, type); + } + } + isAstMathVariable(ast) { const mathProperties = [ 'E', @@ -7334,7 +7327,7 @@ class WebGLFunctionNode extends FunctionNode { } break; } - throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType }`, ast); + throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType } for argument named "${ argument.name }"`, ast); } } retArr.push(')'); @@ -12018,11 +12011,15 @@ const utils = { const start = ast.loc.start; const end = ast.loc.end; const result = []; - result.push(lines[start.line - 1].slice(start.column)); - for (let i = start.line; i < end.line - 1; i++) { - result.push(lines[i]); + if (start.line === end.line) { + result.push(lines[start.line - 1].substring(start.column, end.column)); + } else { + result.push(lines[start.line - 1].slice(start.column)); + for (let i = start.line; i < end.line; i++) { + result.push(lines[i]); + } + result.push(lines[end.line - 1].slice(0, end.column)); } - result.push(lines[end.line - 1].slice(0, end.column)); return result.join('\n'); }, diff --git a/bin/gpu-browser-core.min.js b/bin/gpu-browser-core.min.js index 972823d8..e9c9c602 100644 --- a/bin/gpu-browser-core.min.js +++ b/bin/gpu-browser-core.min.js @@ -4,8 +4,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.16 - * @date Thu Jun 20 2019 10:09:02 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.17 + * @date Mon Jun 24 2019 21:10:12 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -17,8 +17,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.16 - * @date Thu Jun 20 2019 10:09:00 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.17 + * @date Mon Jun 24 2019 21:10:10 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -1690,12 +1690,16 @@ class FunctionBuilder { dynamicOutput, } = kernel; - const lookupReturnType = (functionName, ast, requestingNode) => { - return functionBuilder.lookupReturnType(functionName, ast, requestingNode); + const needsArgumentType = (functionName, index) => { + return functionBuilder.needsArgumentType(functionName, index); }; - const lookupArgumentType = (argumentName, requestingNode) => { - return functionBuilder.lookupArgumentType(argumentName, requestingNode); + const assignArgumentType = (functionName, index, type) => { + functionBuilder.assignArgumentType(functionName, index, type); + }; + + const lookupReturnType = (functionName, ast, requestingNode) => { + return functionBuilder.lookupReturnType(functionName, ast, requestingNode); }; const lookupFunctionArgumentTypes = (functionName) => { @@ -1737,10 +1741,11 @@ class FunctionBuilder { name: ast.id.name, argumentNames, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -1754,10 +1759,11 @@ class FunctionBuilder { isRootKernel: false, onNestedFunction, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -1804,10 +1810,11 @@ class FunctionBuilder { optimizeFloatMemory, precision, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -1987,37 +1994,6 @@ class FunctionBuilder { return this.getStringFromFunctionNames(Object.keys(this.functionMap)); } - lookupArgumentType(argumentName, requestingNode) { - const index = requestingNode.argumentNames.indexOf(argumentName); - if (index === -1) { - return null; - } - if (this.lookupChain.length === 0) { - return null; - } - let link = this.lookupChain[this.lookupChain.length - 1 - this.argumentChain.length]; - if (!link) { - return null; - } - const { - ast, - requestingNode: parentRequestingNode - } = link; - if (ast.arguments.length === 0) { - return null; - } - const usedArgument = ast.arguments[index]; - if (!usedArgument) { - return null; - } - - this.argumentChain.push(argumentName); - - const type = parentRequestingNode.getType(usedArgument); - this.argumentChain.pop(); - return type; - } - lookupReturnType(functionName, ast, requestingNode) { if (ast.type !== 'CallExpression') { throw new Error(`expected ast type of "CallExpression", but is ${ ast.type }`); @@ -2060,6 +2036,7 @@ class FunctionBuilder { } return null; + } _getFunction(functionName) { @@ -2126,6 +2103,12 @@ class FunctionBuilder { } } + needsArgumentType(functionName, i) { + if (!this._isFunction(functionName)) return false; + const fnNode = this._getFunction(functionName); + return !fnNode.argumentTypes[i]; + } + assignArgumentType(functionName, i, argumentType, requestingNode) { if (!this._isFunction(functionName)) return; const fnNode = this._getFunction(functionName); @@ -2241,8 +2224,9 @@ class FunctionNode { this.contexts = null; this.functionCalls = null; this.states = []; + this.needsArgumentType = null; + this.assignArgumentType = null; this.lookupReturnType = null; - this.lookupArgumentType = null; this.lookupFunctionArgumentTypes = null; this.lookupFunctionArgumentBitRatio = null; this.triggerImplyArgumentType = null; @@ -2475,12 +2459,6 @@ class FunctionNode { const argumentType = this.argumentTypes[argumentIndex]; if (argumentType) { type = argumentType; - } else if (this.lookupArgumentType) { - const foundArgumentType = this.lookupArgumentType(ast.name, this); - if (!this.argumentTypes[argumentIndex]) { - this.argumentTypes[argumentIndex] = foundArgumentType; - } - type = foundArgumentType; } } if (!type && this.strictTypingChecking) { @@ -2574,12 +2552,16 @@ class FunctionNode { } if (!ast.callee || !ast.callee.name) { if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[ast.callee.expressions.length - 1].property.name) { - return this.lookupReturnType(ast.callee.expressions[ast.callee.expressions.length - 1].property.name, ast, this); + const functionName = ast.callee.expressions[ast.callee.expressions.length - 1].property.name; + this.inferArgumentTypesIfNeeded(functionName, ast.arguments); + return this.lookupReturnType(functionName, ast, this); } throw this.astErrorOutput('Unknown call expression', ast); } if (ast.callee && ast.callee.name) { - return this.lookupReturnType(ast.callee.name, ast, this); + const functionName = ast.callee.name; + this.inferArgumentTypesIfNeeded(functionName, ast.arguments); + return this.lookupReturnType(functionName, ast, this); } throw this.astErrorOutput(`Unhandled getType Type "${ ast.type }"`, ast); case 'BinaryExpression': @@ -2730,6 +2712,17 @@ class FunctionNode { } } + inferArgumentTypesIfNeeded(functionName, args) { + for (let i = 0; i < args.length; i++) { + if (!this.needsArgumentType(functionName, i)) continue; + const type = this.getType(args[i]); + if (!type) { + throw this.astErrorOutput(`Unable to infer argument ${i}`, args[i]); + } + this.assignArgumentType(functionName, i, type); + } + } + isAstMathVariable(ast) { const mathProperties = [ 'E', @@ -7347,7 +7340,7 @@ class WebGLFunctionNode extends FunctionNode { } break; } - throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType }`, ast); + throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType } for argument named "${ argument.name }"`, ast); } } retArr.push(')'); @@ -12031,11 +12024,15 @@ const utils = { const start = ast.loc.start; const end = ast.loc.end; const result = []; - result.push(lines[start.line - 1].slice(start.column)); - for (let i = start.line; i < end.line - 1; i++) { - result.push(lines[i]); + if (start.line === end.line) { + result.push(lines[start.line - 1].substring(start.column, end.column)); + } else { + result.push(lines[start.line - 1].slice(start.column)); + for (let i = start.line; i < end.line; i++) { + result.push(lines[i]); + } + result.push(lines[end.line - 1].slice(0, end.column)); } - result.push(lines[end.line - 1].slice(0, end.column)); return result.join('\n'); }, diff --git a/bin/gpu-browser.js b/bin/gpu-browser.js index fcbc14b6..853fba17 100644 --- a/bin/gpu-browser.js +++ b/bin/gpu-browser.js @@ -4,8 +4,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.16 - * @date Thu Jun 20 2019 10:09:00 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.17 + * @date Mon Jun 24 2019 21:10:10 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -6441,12 +6441,16 @@ class FunctionBuilder { dynamicOutput, } = kernel; - const lookupReturnType = (functionName, ast, requestingNode) => { - return functionBuilder.lookupReturnType(functionName, ast, requestingNode); + const needsArgumentType = (functionName, index) => { + return functionBuilder.needsArgumentType(functionName, index); }; - const lookupArgumentType = (argumentName, requestingNode) => { - return functionBuilder.lookupArgumentType(argumentName, requestingNode); + const assignArgumentType = (functionName, index, type) => { + functionBuilder.assignArgumentType(functionName, index, type); + }; + + const lookupReturnType = (functionName, ast, requestingNode) => { + return functionBuilder.lookupReturnType(functionName, ast, requestingNode); }; const lookupFunctionArgumentTypes = (functionName) => { @@ -6488,10 +6492,11 @@ class FunctionBuilder { name: ast.id.name, argumentNames, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -6505,10 +6510,11 @@ class FunctionBuilder { isRootKernel: false, onNestedFunction, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -6555,10 +6561,11 @@ class FunctionBuilder { optimizeFloatMemory, precision, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -6738,37 +6745,6 @@ class FunctionBuilder { return this.getStringFromFunctionNames(Object.keys(this.functionMap)); } - lookupArgumentType(argumentName, requestingNode) { - const index = requestingNode.argumentNames.indexOf(argumentName); - if (index === -1) { - return null; - } - if (this.lookupChain.length === 0) { - return null; - } - let link = this.lookupChain[this.lookupChain.length - 1 - this.argumentChain.length]; - if (!link) { - return null; - } - const { - ast, - requestingNode: parentRequestingNode - } = link; - if (ast.arguments.length === 0) { - return null; - } - const usedArgument = ast.arguments[index]; - if (!usedArgument) { - return null; - } - - this.argumentChain.push(argumentName); - - const type = parentRequestingNode.getType(usedArgument); - this.argumentChain.pop(); - return type; - } - lookupReturnType(functionName, ast, requestingNode) { if (ast.type !== 'CallExpression') { throw new Error(`expected ast type of "CallExpression", but is ${ ast.type }`); @@ -6811,6 +6787,7 @@ class FunctionBuilder { } return null; + } _getFunction(functionName) { @@ -6877,6 +6854,12 @@ class FunctionBuilder { } } + needsArgumentType(functionName, i) { + if (!this._isFunction(functionName)) return false; + const fnNode = this._getFunction(functionName); + return !fnNode.argumentTypes[i]; + } + assignArgumentType(functionName, i, argumentType, requestingNode) { if (!this._isFunction(functionName)) return; const fnNode = this._getFunction(functionName); @@ -6992,8 +6975,9 @@ class FunctionNode { this.contexts = null; this.functionCalls = null; this.states = []; + this.needsArgumentType = null; + this.assignArgumentType = null; this.lookupReturnType = null; - this.lookupArgumentType = null; this.lookupFunctionArgumentTypes = null; this.lookupFunctionArgumentBitRatio = null; this.triggerImplyArgumentType = null; @@ -7226,12 +7210,6 @@ class FunctionNode { const argumentType = this.argumentTypes[argumentIndex]; if (argumentType) { type = argumentType; - } else if (this.lookupArgumentType) { - const foundArgumentType = this.lookupArgumentType(ast.name, this); - if (!this.argumentTypes[argumentIndex]) { - this.argumentTypes[argumentIndex] = foundArgumentType; - } - type = foundArgumentType; } } if (!type && this.strictTypingChecking) { @@ -7325,12 +7303,16 @@ class FunctionNode { } if (!ast.callee || !ast.callee.name) { if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[ast.callee.expressions.length - 1].property.name) { - return this.lookupReturnType(ast.callee.expressions[ast.callee.expressions.length - 1].property.name, ast, this); + const functionName = ast.callee.expressions[ast.callee.expressions.length - 1].property.name; + this.inferArgumentTypesIfNeeded(functionName, ast.arguments); + return this.lookupReturnType(functionName, ast, this); } throw this.astErrorOutput('Unknown call expression', ast); } if (ast.callee && ast.callee.name) { - return this.lookupReturnType(ast.callee.name, ast, this); + const functionName = ast.callee.name; + this.inferArgumentTypesIfNeeded(functionName, ast.arguments); + return this.lookupReturnType(functionName, ast, this); } throw this.astErrorOutput(`Unhandled getType Type "${ ast.type }"`, ast); case 'BinaryExpression': @@ -7481,6 +7463,17 @@ class FunctionNode { } } + inferArgumentTypesIfNeeded(functionName, args) { + for (let i = 0; i < args.length; i++) { + if (!this.needsArgumentType(functionName, i)) continue; + const type = this.getType(args[i]); + if (!type) { + throw this.astErrorOutput(`Unable to infer argument ${i}`, args[i]); + } + this.assignArgumentType(functionName, i, type); + } + } + isAstMathVariable(ast) { const mathProperties = [ 'E', @@ -12098,7 +12091,7 @@ class WebGLFunctionNode extends FunctionNode { } break; } - throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType }`, ast); + throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType } for argument named "${ argument.name }"`, ast); } } retArr.push(')'); @@ -16782,11 +16775,15 @@ const utils = { const start = ast.loc.start; const end = ast.loc.end; const result = []; - result.push(lines[start.line - 1].slice(start.column)); - for (let i = start.line; i < end.line - 1; i++) { - result.push(lines[i]); + if (start.line === end.line) { + result.push(lines[start.line - 1].substring(start.column, end.column)); + } else { + result.push(lines[start.line - 1].slice(start.column)); + for (let i = start.line; i < end.line; i++) { + result.push(lines[i]); + } + result.push(lines[end.line - 1].slice(0, end.column)); } - result.push(lines[end.line - 1].slice(0, end.column)); return result.join('\n'); }, diff --git a/bin/gpu-browser.min.js b/bin/gpu-browser.min.js index cbc7373d..38503e71 100644 --- a/bin/gpu-browser.min.js +++ b/bin/gpu-browser.min.js @@ -4,8 +4,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.16 - * @date Thu Jun 20 2019 10:09:02 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.17 + * @date Mon Jun 24 2019 21:10:12 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -17,8 +17,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.16 - * @date Thu Jun 20 2019 10:09:00 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.17 + * @date Mon Jun 24 2019 21:10:10 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -6454,12 +6454,16 @@ class FunctionBuilder { dynamicOutput, } = kernel; - const lookupReturnType = (functionName, ast, requestingNode) => { - return functionBuilder.lookupReturnType(functionName, ast, requestingNode); + const needsArgumentType = (functionName, index) => { + return functionBuilder.needsArgumentType(functionName, index); }; - const lookupArgumentType = (argumentName, requestingNode) => { - return functionBuilder.lookupArgumentType(argumentName, requestingNode); + const assignArgumentType = (functionName, index, type) => { + functionBuilder.assignArgumentType(functionName, index, type); + }; + + const lookupReturnType = (functionName, ast, requestingNode) => { + return functionBuilder.lookupReturnType(functionName, ast, requestingNode); }; const lookupFunctionArgumentTypes = (functionName) => { @@ -6501,10 +6505,11 @@ class FunctionBuilder { name: ast.id.name, argumentNames, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -6518,10 +6523,11 @@ class FunctionBuilder { isRootKernel: false, onNestedFunction, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -6568,10 +6574,11 @@ class FunctionBuilder { optimizeFloatMemory, precision, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -6751,37 +6758,6 @@ class FunctionBuilder { return this.getStringFromFunctionNames(Object.keys(this.functionMap)); } - lookupArgumentType(argumentName, requestingNode) { - const index = requestingNode.argumentNames.indexOf(argumentName); - if (index === -1) { - return null; - } - if (this.lookupChain.length === 0) { - return null; - } - let link = this.lookupChain[this.lookupChain.length - 1 - this.argumentChain.length]; - if (!link) { - return null; - } - const { - ast, - requestingNode: parentRequestingNode - } = link; - if (ast.arguments.length === 0) { - return null; - } - const usedArgument = ast.arguments[index]; - if (!usedArgument) { - return null; - } - - this.argumentChain.push(argumentName); - - const type = parentRequestingNode.getType(usedArgument); - this.argumentChain.pop(); - return type; - } - lookupReturnType(functionName, ast, requestingNode) { if (ast.type !== 'CallExpression') { throw new Error(`expected ast type of "CallExpression", but is ${ ast.type }`); @@ -6824,6 +6800,7 @@ class FunctionBuilder { } return null; + } _getFunction(functionName) { @@ -6890,6 +6867,12 @@ class FunctionBuilder { } } + needsArgumentType(functionName, i) { + if (!this._isFunction(functionName)) return false; + const fnNode = this._getFunction(functionName); + return !fnNode.argumentTypes[i]; + } + assignArgumentType(functionName, i, argumentType, requestingNode) { if (!this._isFunction(functionName)) return; const fnNode = this._getFunction(functionName); @@ -7005,8 +6988,9 @@ class FunctionNode { this.contexts = null; this.functionCalls = null; this.states = []; + this.needsArgumentType = null; + this.assignArgumentType = null; this.lookupReturnType = null; - this.lookupArgumentType = null; this.lookupFunctionArgumentTypes = null; this.lookupFunctionArgumentBitRatio = null; this.triggerImplyArgumentType = null; @@ -7239,12 +7223,6 @@ class FunctionNode { const argumentType = this.argumentTypes[argumentIndex]; if (argumentType) { type = argumentType; - } else if (this.lookupArgumentType) { - const foundArgumentType = this.lookupArgumentType(ast.name, this); - if (!this.argumentTypes[argumentIndex]) { - this.argumentTypes[argumentIndex] = foundArgumentType; - } - type = foundArgumentType; } } if (!type && this.strictTypingChecking) { @@ -7338,12 +7316,16 @@ class FunctionNode { } if (!ast.callee || !ast.callee.name) { if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[ast.callee.expressions.length - 1].property.name) { - return this.lookupReturnType(ast.callee.expressions[ast.callee.expressions.length - 1].property.name, ast, this); + const functionName = ast.callee.expressions[ast.callee.expressions.length - 1].property.name; + this.inferArgumentTypesIfNeeded(functionName, ast.arguments); + return this.lookupReturnType(functionName, ast, this); } throw this.astErrorOutput('Unknown call expression', ast); } if (ast.callee && ast.callee.name) { - return this.lookupReturnType(ast.callee.name, ast, this); + const functionName = ast.callee.name; + this.inferArgumentTypesIfNeeded(functionName, ast.arguments); + return this.lookupReturnType(functionName, ast, this); } throw this.astErrorOutput(`Unhandled getType Type "${ ast.type }"`, ast); case 'BinaryExpression': @@ -7494,6 +7476,17 @@ class FunctionNode { } } + inferArgumentTypesIfNeeded(functionName, args) { + for (let i = 0; i < args.length; i++) { + if (!this.needsArgumentType(functionName, i)) continue; + const type = this.getType(args[i]); + if (!type) { + throw this.astErrorOutput(`Unable to infer argument ${i}`, args[i]); + } + this.assignArgumentType(functionName, i, type); + } + } + isAstMathVariable(ast) { const mathProperties = [ 'E', @@ -12111,7 +12104,7 @@ class WebGLFunctionNode extends FunctionNode { } break; } - throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType }`, ast); + throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType } for argument named "${ argument.name }"`, ast); } } retArr.push(')'); @@ -16795,11 +16788,15 @@ const utils = { const start = ast.loc.start; const end = ast.loc.end; const result = []; - result.push(lines[start.line - 1].slice(start.column)); - for (let i = start.line; i < end.line - 1; i++) { - result.push(lines[i]); + if (start.line === end.line) { + result.push(lines[start.line - 1].substring(start.column, end.column)); + } else { + result.push(lines[start.line - 1].slice(start.column)); + for (let i = start.line; i < end.line; i++) { + result.push(lines[i]); + } + result.push(lines[end.line - 1].slice(0, end.column)); } - result.push(lines[end.line - 1].slice(0, end.column)); return result.join('\n'); }, diff --git a/package.json b/package.json index 7b37d2e0..fbe0e802 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gpu.js", - "version": "2.0.0-rc.16", + "version": "2.0.0-rc.17", "description": "GPU Accelerated JavaScript", "engines": { "node": ">=10.0.0" diff --git a/src/backend/function-builder.js b/src/backend/function-builder.js index 5e3d3fc2..088cd04d 100644 --- a/src/backend/function-builder.js +++ b/src/backend/function-builder.js @@ -37,12 +37,16 @@ class FunctionBuilder { dynamicOutput, } = kernel; - const lookupReturnType = (functionName, ast, requestingNode) => { - return functionBuilder.lookupReturnType(functionName, ast, requestingNode); + const needsArgumentType = (functionName, index) => { + return functionBuilder.needsArgumentType(functionName, index); }; - const lookupArgumentType = (argumentName, requestingNode) => { - return functionBuilder.lookupArgumentType(argumentName, requestingNode); + const assignArgumentType = (functionName, index, type) => { + functionBuilder.assignArgumentType(functionName, index, type); + }; + + const lookupReturnType = (functionName, ast, requestingNode) => { + return functionBuilder.lookupReturnType(functionName, ast, requestingNode); }; const lookupFunctionArgumentTypes = (functionName) => { @@ -84,10 +88,11 @@ class FunctionBuilder { name: ast.id.name, argumentNames, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -101,10 +106,11 @@ class FunctionBuilder { isRootKernel: false, onNestedFunction, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -151,10 +157,11 @@ class FunctionBuilder { optimizeFloatMemory, precision, lookupReturnType, - lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, + needsArgumentType, + assignArgumentType, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, @@ -386,37 +393,6 @@ class FunctionBuilder { return this.getStringFromFunctionNames(Object.keys(this.functionMap)); } - lookupArgumentType(argumentName, requestingNode) { - const index = requestingNode.argumentNames.indexOf(argumentName); - if (index === -1) { - return null; - } - if (this.lookupChain.length === 0) { - return null; - } - let link = this.lookupChain[this.lookupChain.length - 1 - this.argumentChain.length]; - if (!link) { - return null; - } - const { - ast, - requestingNode: parentRequestingNode - } = link; - if (ast.arguments.length === 0) { - return null; - } - const usedArgument = ast.arguments[index]; - if (!usedArgument) { - return null; - } - - this.argumentChain.push(argumentName); - - const type = parentRequestingNode.getType(usedArgument); - this.argumentChain.pop(); - return type; - } - lookupReturnType(functionName, ast, requestingNode) { if (ast.type !== 'CallExpression') { throw new Error(`expected ast type of "CallExpression", but is ${ ast.type }`); @@ -464,6 +440,59 @@ class FunctionBuilder { // function not found, maybe native? return null; + + /** + * first iteration + * kernel.outputs = Array + * kernel.targets = Array + * kernel.returns = null + * kernel.calls.calcErrorOutput = [kernel.output, kernel.targets] + * kernel.calls.calcDeltas = [calcErrorOutput.returns, kernel.output] + * calcErrorOutput.output = null + * calcErrorOutput.targets = null + * calcErrorOutput.returns = null + * calcDeltasSigmoid.error = null + * calcDeltasSigmoid.output = Number + * calcDeltasSigmoid.returns = null + * + * resolvable are: + * calcErrorOutput.output + * calcErrorOutput.targets + * calcErrorOutput.returns + * + * second iteration + * kernel.outputs = Array + * kernel.targets = Array + * kernel.returns = null + * kernel.calls.calcErrorOutput = [kernel.output, kernel.targets] + * kernel.calls.calcDeltas = [calcErrorOutput.returns, kernel.output] + * calcErrorOutput.output = Number + * calcErrorOutput.targets = Array + * calcErrorOutput.returns = Number + * calcDeltasSigmoid.error = null + * calcDeltasSigmoid.output = Number + * calcDeltasSigmoid.returns = null + * + * resolvable are: + * calcDeltasSigmoid.error + * calcDeltasSigmoid.returns + * kernel.returns + * + * third iteration + * kernel.outputs = Array + * kernel.targets = Array + * kernel.returns = Number + * kernel.calls.calcErrorOutput = [kernel.output, kernel.targets] + * kernel.calls.calcDeltas = [calcErrorOutput.returns, kernel.output] + * calcErrorOutput.output = Number + * calcErrorOutput.targets = Array + * calcErrorOutput.returns = Number + * calcDeltasSigmoid.error = Number + * calcDeltasSigmoid.output = Number + * calcDeltasSigmoid.returns = Number + * + * + */ } _getFunction(functionName) { @@ -530,6 +559,12 @@ class FunctionBuilder { } } + needsArgumentType(functionName, i) { + if (!this._isFunction(functionName)) return false; + const fnNode = this._getFunction(functionName); + return !fnNode.argumentTypes[i]; + } + assignArgumentType(functionName, i, argumentType, requestingNode) { if (!this._isFunction(functionName)) return; const fnNode = this._getFunction(functionName); diff --git a/src/backend/function-node.js b/src/backend/function-node.js index 2d6fc6fb..2061ebe9 100644 --- a/src/backend/function-node.js +++ b/src/backend/function-node.js @@ -36,8 +36,9 @@ class FunctionNode { this.contexts = null; this.functionCalls = null; this.states = []; + this.needsArgumentType = null; + this.assignArgumentType = null; this.lookupReturnType = null; - this.lookupArgumentType = null; this.lookupFunctionArgumentTypes = null; this.lookupFunctionArgumentBitRatio = null; this.triggerImplyArgumentType = null; @@ -302,13 +303,6 @@ class FunctionNode { const argumentType = this.argumentTypes[argumentIndex]; if (argumentType) { type = argumentType; - } else if (this.lookupArgumentType) { - const foundArgumentType = this.lookupArgumentType(ast.name, this); - // argumentType may be filled in by now - if (!this.argumentTypes[argumentIndex]) { - this.argumentTypes[argumentIndex] = foundArgumentType; - } - type = foundArgumentType; } } if (!type && this.strictTypingChecking) { @@ -419,12 +413,16 @@ class FunctionNode { } if (!ast.callee || !ast.callee.name) { if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[ast.callee.expressions.length - 1].property.name) { - return this.lookupReturnType(ast.callee.expressions[ast.callee.expressions.length - 1].property.name, ast, this); + const functionName = ast.callee.expressions[ast.callee.expressions.length - 1].property.name; + this.inferArgumentTypesIfNeeded(functionName, ast.arguments); + return this.lookupReturnType(functionName, ast, this); } throw this.astErrorOutput('Unknown call expression', ast); } if (ast.callee && ast.callee.name) { - return this.lookupReturnType(ast.callee.name, ast, this); + const functionName = ast.callee.name; + this.inferArgumentTypesIfNeeded(functionName, ast.arguments); + return this.lookupReturnType(functionName, ast, this); } throw this.astErrorOutput(`Unhandled getType Type "${ ast.type }"`, ast); case 'BinaryExpression': @@ -576,6 +574,18 @@ class FunctionNode { } } + inferArgumentTypesIfNeeded(functionName, args) { + // ensure arguments are filled in, so when we lookup return type, we already can infer it + for (let i = 0; i < args.length; i++) { + if (!this.needsArgumentType(functionName, i)) continue; + const type = this.getType(args[i]); + if (!type) { + throw this.astErrorOutput(`Unable to infer argument ${i}`, args[i]); + } + this.assignArgumentType(functionName, i, type); + } + } + isAstMathVariable(ast) { const mathProperties = [ 'E', diff --git a/src/backend/web-gl/function-node.js b/src/backend/web-gl/function-node.js index 699d0b40..7c4cbe23 100644 --- a/src/backend/web-gl/function-node.js +++ b/src/backend/web-gl/function-node.js @@ -1361,7 +1361,7 @@ class WebGLFunctionNode extends FunctionNode { } break; } - throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType }`, ast); + throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType } for argument named "${ argument.name }"`, ast); } } // Close arguments space diff --git a/src/utils.js b/src/utils.js index e869d280..4995da92 100644 --- a/src/utils.js +++ b/src/utils.js @@ -312,11 +312,15 @@ const utils = { const start = ast.loc.start; const end = ast.loc.end; const result = []; - result.push(lines[start.line - 1].slice(start.column)); - for (let i = start.line; i < end.line - 1; i++) { - result.push(lines[i]); + if (start.line === end.line) { + result.push(lines[start.line - 1].substring(start.column, end.column)); + } else { + result.push(lines[start.line - 1].slice(start.column)); + for (let i = start.line; i < end.line; i++) { + result.push(lines[i]); + } + result.push(lines[end.line - 1].slice(0, end.column)); } - result.push(lines[end.line - 1].slice(0, end.column)); return result.join('\n'); }, diff --git a/test/internal/backend/web-gl/function-node/astCallExpression.js b/test/internal/backend/web-gl/function-node/astCallExpression.js index a7958b15..f667d764 100644 --- a/test/internal/backend/web-gl/function-node/astCallExpression.js +++ b/test/internal/backend/web-gl/function-node/astCallExpression.js @@ -81,6 +81,7 @@ test('handles argument of type Input', () => { } throw new Error(`unhandled triggerTrackArgumentSynonym`); }, + assignArgumentType: () => {} }); assert.equal(node.toString(), 'float kernel(sampler2D user_v) {' + '\nreturn childFunction(user_v);' diff --git a/test/internal/backend/web-gl/function-node/getVariableType.js b/test/internal/backend/web-gl/function-node/getVariableType.js index 62b76a44..f8944092 100644 --- a/test/internal/backend/web-gl/function-node/getVariableType.js +++ b/test/internal/backend/web-gl/function-node/getVariableType.js @@ -19,7 +19,8 @@ test('Native function > detects same native argument type float, and no cast', ( lookupFunctionArgumentTypes: (functionName) => { if (functionName === 'nativeFunction') return argumentTypes; throw new Error('unknown function'); - } + }, + needsArgumentType: () => false }); assert.equal(node.toString(), 'float kernel(float user_value) {' @@ -43,7 +44,8 @@ test('Native function > detects same native argument type int, and no cast', () lookupFunctionArgumentTypes: (functionName) => { if (functionName === 'nativeFunction') return argumentTypes; throw new Error('unknown function'); - } + }, + needsArgumentType: () => false }); assert.equal(node.toString(), 'float kernel(int user_value) {' @@ -67,7 +69,8 @@ test('Native function > detects different native argument type int from literal, lookupFunctionArgumentTypes: (functionName) => { if (functionName === 'nativeFunction') return argumentTypes; throw new Error('unknown function'); - } + }, + needsArgumentType: () => false }); assert.equal(node.toString(), 'float kernel(float user_value) {' @@ -91,7 +94,8 @@ test('Native function > detects different native argument type float from litera lookupFunctionArgumentTypes: (functionName) => { if (functionName === 'nativeFunction') return argumentTypes; throw new Error('unknown function'); - } + }, + needsArgumentType: () => false }); assert.equal(node.toString(), 'float kernel(float user_value) {' @@ -115,7 +119,8 @@ test('Native function > detects different native argument type int, and cast to lookupFunctionArgumentTypes: (functionName) => { if (functionName === 'nativeFunction') return argumentTypes; throw new Error('unknown function'); - } + }, + needsArgumentType: () => false }); assert.equal(node.toString(), 'float kernel(float user_value) {' @@ -139,7 +144,8 @@ test('Native function > detects different native argument type float, and cast t lookupFunctionArgumentTypes: (functionName) => { if (functionName === 'nativeFunction') return argumentTypes; throw new Error('unknown function'); - } + }, + needsArgumentType: () => false }); assert.equal(node.toString(), 'float kernel(int user_value) {' diff --git a/test/internal/deep-types.js b/test/internal/deep-types.js index c17d1fdd..3af4ebff 100644 --- a/test/internal/deep-types.js +++ b/test/internal/deep-types.js @@ -14,19 +14,13 @@ function oneLayerDeepFloat(mode) { const kernel = gpu.createKernel(function(kernelArgument1) { return childFunction(kernelArgument1); }, { output: [1] }); - sinon.spy(FunctionBuilder.prototype, 'lookupArgumentType'); sinon.spy(FunctionBuilder.prototype, 'lookupReturnType'); try { const result = kernel(1.5); assert.equal(result[0], 2.5); - - assert.equal(FunctionBuilder.prototype.lookupArgumentType.callCount, 1); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[0][0], 'childFunctionArgument1'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 1); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'childFunction'); } finally { - FunctionBuilder.prototype.lookupArgumentType.restore(); FunctionBuilder.prototype.lookupReturnType.restore(); } } @@ -55,21 +49,15 @@ function twoLayerDeepFloat(mode) { const kernel = gpu.createKernel(function(kernelArgument1) { return child1Function(kernelArgument1); }, { output: [1] }); - sinon.spy(FunctionBuilder.prototype, 'lookupArgumentType'); sinon.spy(FunctionBuilder.prototype, 'lookupReturnType'); try { const result = kernel(1.5); assert.equal(result[0], 2.5); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.callCount, 2); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[0][0], 'child2FunctionArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[1][0], 'child1FunctionArgument1'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 3); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child1Function'); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'child2Function'); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'child2Function'); } finally { - FunctionBuilder.prototype.lookupArgumentType.restore(); FunctionBuilder.prototype.lookupReturnType.restore(); } } @@ -98,23 +86,16 @@ function twoArgumentLayerDeepFloat(mode) { const kernel = gpu.createKernel(function(kernelArgument1) { return child1Function(child2Function(kernelArgument1)); }, { output: [1] }); - sinon.spy(FunctionBuilder.prototype, 'lookupArgumentType'); sinon.spy(FunctionBuilder.prototype, 'lookupReturnType'); try { const result = kernel(1.5); assert.equal(kernel.returnType, 'Float'); assert.equal(result[0], 3.5); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.callCount, 2); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[0][0], 'child1FunctionArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[1][0], 'child2FunctionArgument1'); - - assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 4); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child1Function'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'child2Function'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 3); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child2Function'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'child1Function'); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'child2Function'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'child2Function'); } finally { - FunctionBuilder.prototype.lookupArgumentType.restore(); FunctionBuilder.prototype.lookupReturnType.restore(); } } @@ -148,16 +129,10 @@ function threeLayerDeepFloat(mode) { const kernel = gpu.createKernel(function(kernelArgument1) { return child1Function(kernelArgument1); }, { output: [1] }); - sinon.spy(FunctionBuilder.prototype, 'lookupArgumentType'); sinon.spy(FunctionBuilder.prototype, 'lookupReturnType'); try { const result = kernel(1.5); assert.equal(result[0], 3.5); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.callCount, 3); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[0][0], 'child3FunctionArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[1][0], 'child2FunctionArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[2][0], 'child1FunctionArgument1'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 5); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child1Function'); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'child2Function'); @@ -165,7 +140,6 @@ function threeLayerDeepFloat(mode) { assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'child2Function'); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[4][0], 'child3Function'); } finally { - FunctionBuilder.prototype.lookupArgumentType.restore(); FunctionBuilder.prototype.lookupReturnType.restore(); } } @@ -198,26 +172,17 @@ function threeArgumentLayerDeepFloat(mode) { const kernel = gpu.createKernel(function(kernelArgument1) { return child1Function(child2Function(child3Function(kernelArgument1))); }, { output: [1] }); - sinon.spy(FunctionBuilder.prototype, 'lookupArgumentType'); sinon.spy(FunctionBuilder.prototype, 'lookupReturnType'); try { const result = kernel(1.5); assert.equal(result[0], 4.5); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.callCount, 3); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[0][0], 'child1FunctionArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[1][0], 'child2FunctionArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[2][0], 'child3FunctionArgument1'); - - assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 7); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child1Function'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 5); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child3Function'); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'child2Function'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'child2Function'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'child3Function'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'child1Function'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'child2Function'); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[4][0], 'child3Function'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[5][0], 'child2Function'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[6][0], 'child3Function'); } finally { - FunctionBuilder.prototype.lookupArgumentType.restore(); FunctionBuilder.prototype.lookupReturnType.restore(); } } @@ -253,26 +218,17 @@ function threeArgumentLayerDeepNumberTexture1(mode) { const kernel = gpu.createKernel(function(kernelArgument1) { return child1Function(child2Function(child3Function(kernelArgument1))); }, { output: [1] }); - sinon.spy(FunctionBuilder.prototype, 'lookupArgumentType'); sinon.spy(FunctionBuilder.prototype, 'lookupReturnType'); try { const result = kernel(texture); assert.equal(result[0], 4.5); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.callCount, 3); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[0][0], 'child1FunctionArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[1][0], 'child2FunctionArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[2][0], 'child3FunctionArgument1'); - - assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 7); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child1Function'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 5); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child3Function'); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'child2Function'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'child2Function'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'child3Function'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'child1Function'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'child2Function'); assert.equal(FunctionBuilder.prototype.lookupReturnType.args[4][0], 'child3Function'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[5][0], 'child2Function'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[6][0], 'child3Function'); } finally { - FunctionBuilder.prototype.lookupArgumentType.restore(); FunctionBuilder.prototype.lookupReturnType.restore(); } } @@ -517,7 +473,7 @@ function testTortureTest(mode) { .addFunction(addFloatFloat); const texture = gpu.createKernel(function() { return 2; }, { output: [1], precision: 'single' })(); - sinon.spy(FunctionBuilder.prototype, 'lookupArgumentType'); + // sinon.spy(FunctionBuilder.prototype, 'lookupArgumentType'); sinon.spy(FunctionBuilder.prototype, 'lookupReturnType'); try { @@ -527,33 +483,23 @@ function testTortureTest(mode) { const result = kernel([1], texture, [3], 4, new Float32Array([5])); assert.equal(result[0], 1 + 2 + 3 + 4 + 5); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.callCount, 4); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.returnValues.length, 4); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[0][0], 'addFloatFloatArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.returnValues[0], 'Float'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[1][0], 'addArrayFloatArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.returnValues[1], 'Array'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[2][0], 'addFloatArrayArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.returnValues[2], 'Number'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.args[3][0], 'addArrayArrayArgument1'); - assert.equal(FunctionBuilder.prototype.lookupArgumentType.returnValues[3], 'Number'); - - assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 6); - assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues.length, 6); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'addFloatFloat'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[0], 'Float'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'addArrayFloat'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 7); + assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues.length, 7); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'addArrayArray'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[0], 'Number'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'addFloatArray'); assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[1], 'Number'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'addFloatArray'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'addArrayFloat'); assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[2], 'Number'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'addArrayArray'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[3], 'Number'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[4][0], 'addArrayArray'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'addFloatFloat'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[3], 'Float'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[4][0], 'addArrayFloat'); assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[4], 'Number'); - assert.equal(FunctionBuilder.prototype.lookupReturnType.args[5][0], 'addArrayArray'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[5][0], 'addFloatArray'); assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[5], 'Number'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.args[6][0], 'addArrayArray'); + assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[6], 'Number'); } finally { - FunctionBuilder.prototype.lookupArgumentType.restore(); FunctionBuilder.prototype.lookupReturnType.restore(); } } @@ -581,3 +527,41 @@ function testTortureTest(mode) { test('torture test cpu', () => { testTortureTest('cpu'); }); + +function testKernelMap(mode) { + const gpu = new GPU({ mode }); + function calc1(v1, v2) { + return v2[this.thread.x] - v1; + } + function calc2(v1, v2) { + return v1 * v2; + } + const kernelMap = gpu.createKernelMap({ + calc1, + calc2, + }, function (outputs, targets) { + const output = outputs[this.thread.x]; + return calc2(calc1(output, targets), output); + }, { output: [1], pipeline: true }); + try { + const result = kernelMap([1], [3]); + assert.equal(result.calc1.toArray()[0], 2); + assert.equal(result.calc2.toArray()[0], 2); + assert.equal(result.result.toArray()[0], 2); + } finally { + gpu.destroy(); + } +} + + +(GPU.isWebGLSupported ? test : skip)('kernel map webgl', () => { + testKernelMap('webgl'); +}); + +(GPU.isWebGL2Supported ? test : skip)('kernel map webgl2', () => { + testKernelMap('webgl2'); +}); + +(GPU.isHeadlessGLSupported ? test : skip)('kernel map headlessgl', () => { + testKernelMap('headlessgl'); +});