🏡 index : cold-air-networks/mirror-jetstream.git

/*
 * 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";

// FIXME: This should have sensible behavior when it encounters definitions that it cannot handle. Right
// now we are hackishly preventing this by wrapping things in TypeRef. That's probably wrong.
// https://bugs.webkit.org/show_bug.cgi?id=176208
class Rewriter {
    constructor()
    {
        this._mapping = new Map();
    }
    
    _mapNode(oldItem, newItem)
    {
        this._mapping.set(oldItem, newItem);
        return newItem;
    }
    
    _getMapping(oldItem)
    {
        let result = this._mapping.get(oldItem);
        if (result)
            return result;
        return oldItem;
    }
    
    // We return identity for anything that is not inside a function/struct body. When processing
    // function bodies, we only recurse into them and never out of them - for example if there is a
    // function call to another function then we don't rewrite the other function. This is how we stop
    // that.
    visitFuncDef(node) { return node; }
    visitNativeFunc(node) { return node; }
    visitNativeFuncInstance(node) { return node; }
    visitNativeType(node) { return node; }
    visitTypeDef(node) { return node; }
    visitStructType(node) { return node; }
    visitConstexprTypeParameter(node) { return node; }
    visitProtocolDecl(node) { return node; }
    visitEnumType(node) { return node; }

    // This is almost wrong. We instantiate Func in Substitution in ProtocolDecl. Then, we end up
    // not rewriting type variables. I think that just works because not rewriting them there is OK.
    // Everywhere else, it's mandatory that we don't rewrite these because we always assume that
    // type variables are outside the scope of rewriting.
    visitTypeVariable(node) { return node; }

    visitProtocolFuncDecl(node)
    {
        let result = new ProtocolFuncDecl(
            node.origin, node.name,
            node.returnType.visit(this),
            node.typeParameters.map(parameter => parameter.visit(this)),
            node.parameters.map(parameter => parameter.visit(this)),
            node.isCast,
            node.shaderType);
        result.protocolDecl = node.protocolDecl;
        result.possibleOverloads = node.possibleOverloads;
        return result;
    }
    
    visitNativeTypeInstance(node)
    {
        return new NativeTypeInstance(
            node.type.visit(this),
            node.typeArguments.map(argument => argument.visit(this)));
    }
    
    visitFuncParameter(node)
    {
        let result = new FuncParameter(node.origin, node.name, node.type.visit(this));
        this._mapNode(node, result);
        result.ePtr = node.ePtr;
        return result;
    }
    
    visitVariableDecl(node)
    {
        let result = new VariableDecl(
            node.origin, node.name,
            node.type.visit(this),
            Node.visit(node.initializer, this));
        this._mapNode(node, result);
        result.ePtr = node.ePtr;
        return result;
    }
    
    visitBlock(node)
    {
        let result = new Block(node.origin);
        for (let statement of node.statements)
            result.add(statement.visit(this));
        return result;
    }
    
    visitCommaExpression(node)
    {
        return new CommaExpression(node.origin, node.list.map(expression => {
            let result = expression.visit(this);
            if (!result)
                throw new Error("Null result from " + expression);
            return result;
        }));
    }
    
    visitProtocolRef(node)
    {
        return node;
    }
    
    visitTypeRef(node)
    {
        let result = new TypeRef(node.origin, node.name, node.typeArguments.map(typeArgument => typeArgument.visit(this)));
        result.type = Node.visit(node.type, this);
        return result;
    }
    
    visitField(node)
    {
        return new Field(node.origin, node.name, node.type.visit(this));
    }
    
    visitEnumMember(node)
    {
        return new EnumMember(node.origin, node.name, node.value.visit(this));
    }
    
    visitEnumLiteral(node)
    {
        let result = new EnumLiteral(node.origin, node.member);
        result.ePtr = node.ePtr;
        return result;
    }
    
    visitReferenceType(node)
    {
        return new node.constructor(node.origin, node.addressSpace, node.elementType.visit(this));
    }
    
    visitPtrType(node)
    {
        return this.visitReferenceType(node);
    }
    
    visitArrayRefType(node)
    {
        return this.visitReferenceType(node);
    }
    
    visitArrayType(node)
    {
        return new ArrayType(node.origin, node.elementType.visit(this), node.numElements.visit(this));
    }
    
    visitAssignment(node)
    {
        let result = new Assignment(node.origin, node.lhs.visit(this), node.rhs.visit(this));
        result.type = Node.visit(node.type, this);
        return result;
    }
    
    visitReadModifyWriteExpression(node)
    {
        let result = new ReadModifyWriteExpression(node.origin, node.lValue.visit(this));
        result.oldValueVar = node.oldValueVar.visit(this);
        result.newValueVar = node.newValueVar.visit(this);
        result.newValueExp = node.newValueExp.visit(this);
        result.resultExp = node.resultExp.visit(this);
        return result;
    }
    
    visitDereferenceExpression(node)
    {
        let result = new DereferenceExpression(node.origin, node.ptr.visit(this));
        result.type = Node.visit(node.type, this);
        result.addressSpace = node.addressSpace;
        return result;
    }
    
    _handlePropertyAccessExpression(result, node)
    {
        result.possibleGetOverloads = node.possibleGetOverloads;
        result.possibleSetOverloads = node.possibleSetOverloads;
        result.possibleAndOverloads = node.possibleAndOverloads;
        result.baseType = Node.visit(node.baseType, this);
        result.callForGet = Node.visit(node.callForGet, this);
        result.resultTypeForGet = Node.visit(node.resultTypeForGet, this);
        result.callForAnd = Node.visit(node.callForAnd, this);
        result.resultTypeForAnd = Node.visit(node.resultTypeForAnd, this);
        result.callForSet = Node.visit(node.callForSet, this);
        result.errorForSet = node.errorForSet;
        result.updateCalls();
    }
    
    visitDotExpression(node)
    {
        let result = new DotExpression(node.origin, node.struct.visit(this), node.fieldName);
        this._handlePropertyAccessExpression(result, node);
        return result;
    }
    
    visitIndexExpression(node)
    {
        let result = new IndexExpression(node.origin, node.array.visit(this), node.index.visit(this));
        this._handlePropertyAccessExpression(result, node);
        return result;
    }
    
    visitMakePtrExpression(node)
    {
        let result = new MakePtrExpression(node.origin, node.lValue.visit(this));
        result.ePtr = node.ePtr;
        return result;
    }
    
    visitMakeArrayRefExpression(node)
    {
        let result = new MakeArrayRefExpression(node.origin, node.lValue.visit(this));
        if (node.numElements)
            result.numElements = node.numElements.visit(this);
        result.ePtr = node.ePtr;
        return result;
    }
    
    visitConvertPtrToArrayRefExpression(node)
    {
        let result = new ConvertPtrToArrayRefExpression(node.origin, node.lValue.visit(this));
        result.ePtr = node.ePtr;
        return result;
    }
    
    visitVariableRef(node)
    {
        let result = new VariableRef(node.origin, node.name);
        result.variable = this._getMapping(node.variable);
        return result;
    }
    
    visitReturn(node)
    {
        return new Return(node.origin, Node.visit(node.value, this));
    }
    
    visitContinue(node)
    {
        return new Continue(node.origin);
    }
    
    visitBreak(node)
    {
        return new Break(node.origin);
    }

    visitTrapStatement(node)
    {
        return new TrapStatement(node.origin);
    }
    
    visitGenericLiteral(node)
    {
        // FIXME: This doesn't seem right.
        let result = new IntLiteral(node.origin, node.value);
        result.type = node.type.visit(this);
        result.ePtr = node.ePtr;
        return result;
    }
    
    visitGenericLiteralType(node)
    {
        let result = new node.constructor(node.origin, node.value);
        result.type = Node.visit(node.type, this);
        result.preferredType = node.preferredType.visit(this);
        return result;
    }

    visitBoolLiteral(node)
    {
        return node;
    }
    
    visitNullLiteral(node)
    {
        let result = new NullLiteral(node.origin);
        result.type = node.type.visit(this);
        result.ePtr = node.ePtr;
        return result;
    }
    
    visitNullType(node)
    {
        let result = new NullType(node.origin);
        result.type = Node.visit(node.type, this);
        return result;
    }

    processDerivedCallData(node, result)
    {
        let handleTypeArguments = actualTypeArguments => {
            if (actualTypeArguments)
                return actualTypeArguments.map(actualTypeArgument => actualTypeArgument.visit(this));
            else
                return null;
        }
        result.actualTypeArguments = handleTypeArguments(node.actualTypeArguments);
        result.instantiatedActualTypeArguments = handleTypeArguments(node.instantiatedActualTypeArguments);
        let argumentTypes = node.argumentTypes;
        if (argumentTypes)
            result.argumentTypes = argumentTypes.map(argumentType => argumentType.visit(this));
        result.func = node.func;
        result.nativeFuncInstance = node.nativeFuncInstance;
        result.possibleOverloads = node.possibleOverloads;
        if (node.isCast)
            result.setCastData(node.returnType.visit(this));
        result.resultType = Node.visit(node.resultType, this);
        result.resultEPtr = node.resultEPtr;
        return result;
    }

    visitCallExpression(node)
    {
        let result = new CallExpression(
            node.origin, node.name,
            node.typeArguments.map(typeArgument => typeArgument.visit(this)),
            node.argumentList.map(argument => Node.visit(argument, this)));
        return this.processDerivedCallData(node, result);
    }
    
    visitFunctionLikeBlock(node)
    {
        let result = new FunctionLikeBlock(
            node.origin,
            Node.visit(node.returnType, this),
            node.argumentList.map(argument => argument.visit(this)),
            node.parameters.map(parameter => parameter.visit(this)),
            node.body.visit(this));
        result.returnEPtr = node.returnEPtr;
        return result;
    }
    
    visitLogicalNot(node)
    {
        let result = new LogicalNot(node.origin, node.operand.visit(this));
        result.ePtr = node.ePtr;
        return result;
    }
    
    visitLogicalExpression(node)
    {
        let result = new LogicalExpression(node.origin, node.text, node.left.visit(this), node.right.visit(this));
        result.ePtr = node.ePtr;
        return result;
    }

    visitIfStatement(node)
    {
        return new IfStatement(node.origin, node.conditional.visit(this), node.body.visit(this), Node.visit(node.elseBody, this));
    }

    visitWhileLoop(node)
    {
        return new WhileLoop(node.origin, node.conditional.visit(this), node.body.visit(this));
    }

    visitDoWhileLoop(node)
    {
        return new DoWhileLoop(node.origin, node.body.visit(this), node.conditional.visit(this));
    }

    visitForLoop(node)
    {
        return new ForLoop(node.origin,
            Node.visit(node.initialization, this),
            Node.visit(node.condition, this),
            Node.visit(node.increment, this),
            node.body.visit(this));
    }
    
    visitSwitchStatement(node)
    {
        let result = new SwitchStatement(node.origin, Node.visit(node.value, this));
        for (let switchCase of node.switchCases)
            result.add(switchCase.visit(this));
        result.type = Node.visit(node.type, this);
        return result;
    }
    
    visitSwitchCase(node)
    {
        return new SwitchCase(node.origin, Node.visit(node.value, this), node.body.visit(this));
    }
    
    visitAnonymousVariable(node)
    {
        let result = new AnonymousVariable(node.origin, node.type.visit(this));
        result._index = node._index;
        this._mapNode(node, result);
        result.ePtr = node.ePtr;
        return result;
    }
    
    visitIdentityExpression(node)
    {
        return new IdentityExpression(node.target.visit(this));
    }
}