OutlineStatementMutation implements Mutation { @Override public void apply() { BinaryExpr binaryExpr = (BinaryExpr) toOutline.getExpr(); assert binaryExpr.getOp() == BinOp.ASSIGN; final List<String> referencedVariables = getReferencedVariables(binaryExpr.getRhs()); final String newFunctionName = Constants.OUTLINED_FUNCTION_PREFIX + idGenerator.freshId(); toOutline.setExpr(new BinaryExpr( binaryExpr.getLhs().clone(), new FunctionCallExpr(newFunctionName, referencedVariables.stream().map(VariableIdentifierExpr::new) .collect(Collectors.toList())), BinOp.ASSIGN)); final List<ParameterDecl> params = new ArrayList<>(); for (String v : referencedVariables) { final Type type = scopeOfStmt.lookupType(v); assert type != null; final Type varType = type.clone().getWithoutQualifiers(); assert !(varType.getWithoutQualifiers() instanceof ArrayType); params.add(new ParameterDecl(v, maybeGetStructNameType(varType), null)); } Type returnType = scopeOfStmt.lookupType( ((VariableIdentifierExpr) binaryExpr.getLhs()).getName()); if (returnType == null) { final Optional<Type> maybeTypeOfBuiltin = Typer.maybeGetTypeOfBuiltinVariable(((VariableIdentifierExpr) binaryExpr.getLhs()) .getName()); if (maybeTypeOfBuiltin.isPresent()) { returnType = maybeTypeOfBuiltin.get(); } else { throw new RuntimeException("No type for " + ((VariableIdentifierExpr) binaryExpr.getLhs()).getName()); } } returnType = maybeGetStructNameType(returnType.getWithoutQualifiers()); tu.addDeclarationBefore(new FunctionDefinition(new FunctionPrototype( newFunctionName, returnType, params), new BlockStmt(Collections.singletonList( new ReturnStmt(binaryExpr.getRhs().clone())), false)), enclosingFunction); } OutlineStatementMutation(ExprStmt toOutline, Scope scopeOfStmt, TranslationUnit tu,
                                  FunctionDefinition enclosingFunction,
                                  IdGenerator idGenerator); @Override void apply();  }
@Test public void apply() throws Exception { final ExprStmt toOutline = new ExprStmt(new BinaryExpr( new VariableIdentifierExpr("x"), new BinaryExpr(new BinaryExpr(new VariableIdentifierExpr("x"), new VariableIdentifierExpr("y"), BinOp.MUL), new VariableIdentifierExpr("z"), BinOp.ADD), BinOp.ASSIGN)); final Scope fakeScope = new Scope(); fakeScope.add("x", BasicType.VEC2, Optional.empty()); fakeScope.add("y", BasicType.FLOAT, Optional.empty()); fakeScope.add("z", BasicType.VEC2, Optional.empty()); new OutlineStatementMutation(toOutline, fakeScope, tu, fakeFunction, new IdGenerator()).apply(); final String expectedDecl = "vec2 " + Constants.OUTLINED_FUNCTION_PREFIX + "0(vec2 x, float y, vec2 z)\n" + "{\n" + PrettyPrinterVisitor.defaultIndent(1) + "return x * y + z;\n" + "}\n"; assertEquals(expectedDecl, tu.getTopLevelDeclarations().get(0).getText()); assertEquals("x = " + Constants.OUTLINED_FUNCTION_PREFIX + "0(x, y, z);\n", toOutline.getText()); }
@Test public void apply2() throws Exception { ExprStmt toOutline = new ExprStmt(new BinaryExpr( new VariableIdentifierExpr("x"), new VariableIdentifierExpr("y"), BinOp.ASSIGN)); Scope fakeScope = new Scope(); fakeScope.add("x", BasicType.VEC2, Optional.empty()); fakeScope.add("y", new QualifiedType(BasicType.VEC2, Collections.singletonList(TypeQualifier.UNIFORM)), Optional.empty()); new OutlineStatementMutation(toOutline, fakeScope, tu, fakeFunction, new IdGenerator()).apply(); final String expectedDecl = "vec2 " + Constants.OUTLINED_FUNCTION_PREFIX + "0(vec2 y)\n" + "{\n" + PrettyPrinterVisitor.defaultIndent(1) + "return y;\n" + "}\n"; assertEquals(expectedDecl, tu.getTopLevelDeclarations().get(0).getText()); assertEquals("x = " + Constants.OUTLINED_FUNCTION_PREFIX + "0(y);\n", toOutline.getText()); }
@Test public void apply3() throws Exception { ExprStmt toOutline = new ExprStmt(new BinaryExpr( new VariableIdentifierExpr("x"), new BinaryExpr(new VariableIdentifierExpr("x"), new VariableIdentifierExpr("x"), BinOp.ADD), BinOp.ASSIGN)); Scope fakeScope = new Scope(); fakeScope.add("x", BasicType.VEC2, Optional.empty()); new OutlineStatementMutation(toOutline, fakeScope, tu, fakeFunction, new IdGenerator()).apply(); final String expectedDecl = "vec2 " + Constants.OUTLINED_FUNCTION_PREFIX + "0(vec2 x)\n" + "{\n" + PrettyPrinterVisitor.defaultIndent(1) + "return x + x;\n" + "}\n"; assertEquals(expectedDecl, tu.getTopLevelDeclarations().get(0).getText()); assertEquals("x = " + Constants.OUTLINED_FUNCTION_PREFIX + "0(x);\n", toOutline.getText()); }
@Test public void apply4() throws Exception { ExprStmt toOutline = new ExprStmt(new BinaryExpr( new VariableIdentifierExpr("x"), new BinaryExpr(new VariableIdentifierExpr("x"), new VariableIdentifierExpr("x"), BinOp.ADD), BinOp.ASSIGN)); Scope fakeScope = new Scope(); fakeScope.add("x", new QualifiedType(BasicType.VEC2, Collections.singletonList(TypeQualifier.OUT_PARAM)), Optional.empty()); new OutlineStatementMutation(toOutline, fakeScope, tu, fakeFunction, new IdGenerator()).apply(); final String expectedDecl = "vec2 " + Constants.OUTLINED_FUNCTION_PREFIX + "0(vec2 x)\n" + "{\n" + PrettyPrinterVisitor.defaultIndent(1) + "return x + x;\n" + "}\n"; assertEquals(expectedDecl, tu.getTopLevelDeclarations().get(0).getText()); assertEquals("x = " + Constants.OUTLINED_FUNCTION_PREFIX + "0(x);\n", toOutline.getText()); }
@Test public void apply5() throws Exception { ExprStmt toOutline = new ExprStmt(new BinaryExpr( new VariableIdentifierExpr(OpenGlConstants.GL_POINT_SIZE), new BinaryExpr(new VariableIdentifierExpr("x"), new VariableIdentifierExpr("x"), BinOp.ADD), BinOp.ASSIGN)); Scope fakeScope = new Scope(); fakeScope.add("x", BasicType.FLOAT, Optional.empty()); new OutlineStatementMutation(toOutline, fakeScope, tu, fakeFunction, new IdGenerator()).apply(); final String expectedDecl = "float " + Constants.OUTLINED_FUNCTION_PREFIX + "0(float x)\n" + "{\n" + PrettyPrinterVisitor.defaultIndent(1) + "return x + x;\n" + "}\n"; assertEquals(expectedDecl, tu.getTopLevelDeclarations().get(0).getText()); assertEquals(OpenGlConstants.GL_POINT_SIZE + " = " + Constants.OUTLINED_FUNCTION_PREFIX + "0" + "(x);\n", toOutline.getText()); }