153 lines
6.8 KiB
JavaScript
153 lines
6.8 KiB
JavaScript
/*
|
|
* Copyright (C) 2017 Apple Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
"use strict";
|
|
|
|
class FuncInstantiator {
|
|
constructor(program)
|
|
{
|
|
this._program = program;
|
|
this._instances = new Map();
|
|
}
|
|
|
|
// Returns a Func object that uniquely identifies a particular system of type arguments. You must
|
|
// intantiate things with concrete types, because this code casually assumes this. Note that this
|
|
// will return a different func from `func` no matter what. This ensures that we can use the
|
|
// returned func for rewrites that instantiate types without destroying our ability to do overload
|
|
// resolutions on the original Program.
|
|
getUnique(func, typeArguments)
|
|
{
|
|
class FindTypeVariable extends Visitor {
|
|
visitTypeRef(node)
|
|
{
|
|
for (let typeArgument of node.typeArguments)
|
|
typeArgument.visit(this);
|
|
}
|
|
|
|
visitTypeVariable(node) {
|
|
throw new Error("Unexpected type variable: " + node + " when instantiating " + func + " with arguments " + typeArguments);
|
|
}
|
|
}
|
|
for (let typeArgument of typeArguments)
|
|
typeArgument.visit(new FindTypeVariable());
|
|
|
|
let instances = this._instances.get(func);
|
|
if (!instances)
|
|
this._instances.set(func, instances = []);
|
|
|
|
for (let instance of instances) {
|
|
let ok = true;
|
|
for (let i = instance.typeArguments.length; i--;) {
|
|
if (!instance.typeArguments[i].equals(typeArguments[i])) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!ok)
|
|
continue;
|
|
return instance.func;
|
|
}
|
|
|
|
let thisInstantiator = this;
|
|
|
|
class InstantiationSubstitution extends Substitution {
|
|
visitCallExpression(node)
|
|
{
|
|
let result = super.visitCallExpression(node);
|
|
|
|
// We may have to re-resolve the function call, if it was a call to a protocol
|
|
// signature.
|
|
if (result.func instanceof ProtocolFuncDecl) {
|
|
let overload = resolveOverloadImpl(result.possibleOverloads, result.typeArguments, result.argumentTypes, result.returnType);
|
|
if (!overload.func)
|
|
throw new Error("Could not resolve protocol signature function call during instantiation: " + result.func + (overload.failures.length ? "; tried:\n" + overload.failures.join("\n") : ""));
|
|
result.resolveToOverload(overload);
|
|
}
|
|
|
|
if (result.func.isNative)
|
|
result.nativeFuncInstance = thisInstantiator.getUnique(result.func, result.actualTypeArguments);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
let substitution = new InstantiationSubstitution(func.typeParameters, typeArguments);
|
|
|
|
class InstantiationInstantiateImmediates extends InstantiateImmediates {
|
|
visitCallExpression(node)
|
|
{
|
|
// We need to preserve certain things that would have instantiated, but that we cannot
|
|
// instantiate without breaking chain-instantiations (generic function calls generic
|
|
// function so therefore the instantiated generic function must still have the original
|
|
// (uninstantiated) types to instantiate the generic function that it calls).
|
|
let result = new CallExpression(
|
|
node.origin, node.name, node.typeArguments,
|
|
node.argumentList.map(argument => Node.visit(argument, this)));
|
|
result = this.processDerivedCallData(node, result);
|
|
|
|
result.argumentTypes = Array.from(node.argumentTypes);
|
|
if (node.isCast)
|
|
result.setCastData(node.returnType);
|
|
result.actualTypeArguments = Array.from(node.actualTypeArguments);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
let instantiateImmediates = new InstantiationInstantiateImmediates();
|
|
|
|
class Instantiate {
|
|
visitFuncDef(func)
|
|
{
|
|
let returnType = func.returnType.visit(substitution);
|
|
returnType = returnType.visit(instantiateImmediates);
|
|
let parameters = func.parameters.map(parameter => parameter.visit(substitution));
|
|
parameters = parameters.map(parameter => parameter.visit(instantiateImmediates));
|
|
let body = func.body.visit(substitution);
|
|
body = body.visit(instantiateImmediates);
|
|
return new FuncDef(
|
|
func.origin, func.name, returnType, [], parameters, body, func.isCast,
|
|
func.shaderType);
|
|
}
|
|
|
|
visitNativeFunc(func)
|
|
{
|
|
return new NativeFuncInstance(
|
|
func,
|
|
func.returnType.visit(substitution).visit(instantiateImmediates),
|
|
func.parameters.map(parameter => parameter.visit(substitution).visit(instantiateImmediates)),
|
|
func.isCast,
|
|
func.shaderType,
|
|
func.instantiateImplementation(substitution));
|
|
}
|
|
}
|
|
let resultingFunc = func.visit(new Instantiate());
|
|
resultingFunc.uninstantiatedReturnType = func.returnType.visit(substitution);
|
|
let instance = {func: resultingFunc, typeArguments};
|
|
instances.push(instance);
|
|
return resultingFunc;
|
|
}
|
|
}
|
|
|