| /* |
| * 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)); |
| } |
| } |
| |