Skip to content

Modelling APIs with Coko

To evaluate rules for an API, Coko needs an understanding of the components of the API, such as classes or functions. In Coko these components are modelled through interfaces, classes and a class called Op.

Ops

Ops are the basic building blocks for writing policies in Coko. With Ops you can model and group functions of the API that serve a similar functionality. They are also a way to define queries to the Codyze backend for finding calls to these functions. Each Op object is one query to the backend.

There are currently two types of Ops, FunctionOps for modelling functions and ConstructorOps for modelling constructors in object-oriented languages. They are both built with type-safe builders. The following sections will explain the builders for each Op type.

FunctionOps

The function op() is the start for building FunctionOps. Within the block of op() the fully qualified name of the functions you want to model can be specified as a string. In the block of the fully qualified name the arguments to function can be defined. They serve as a filter for the query. Only calls to the function with the same number of arguments with the same value at each position will be found by the query.

In signature it is also possible to specify unordered arguments. These are arguments, that should somehow be passed to the function calls we want to find, but it is not important, in which position they are passed.

Example of defining a FunctionOp
op {
    "my.fully.qualified.name" { 
        signature(5..10) 
        signature(".*one.*") 
        signature(0..5, listOf("one", "two")) 
        signature() 
    }

    "my.other.fully.qualified.name" { 
        signature { 
            - ".*" 
            -7 
        }
        signature(arrayOf(4)) { 
            - 123
        }
    }
}

ConstructorOps

The function of the builder for ConstructorOps is constructor() . The fully qualified name of the class is the first argument. In the block of constructor() you can specify the arguments to the constructor like for the FunctionOp. They serve the same purpose as for FunctionOps.

Example of defining a ConstructorOp
constructor("my.fully.qualified.MyClass") { 
    signature() 
}

Special argument types

Arguments in Ops are used to filter calls of a function based on the arguments that are given to the calls. In Coko there are a few special argument types that help with the filtering.

The first type is the Wildcard object. If the Wildcard object is given as an argument to Op, the filter will allow any kind of value in the same argument position of the function calls.

The second type is null. It can be used to signify that the given arguments should be filtered out of the query. This type will be helpful when constructing Op templates with Coko functions.

Another special type are ParameterGroups. They are a filter to express that the argument at the position should be composed of multiple values. An example would be if the argument is a string that should contain multiple strings, for example foo("Start string" + 12 + "End string"). This can be modeled with ParameterGroups. Coko offers the DSL function group, in which these values can be specified.

The last types are the Type class and the ParamWithType class. The Type class is used to filter the type of the arguments. The fully qualified name of the type must be given ParamWithType combines the filter for the value with the filter for the type.

Example with special argument types
op {
    "my.fully.qualified.name" {
        signature(Wildcard) 
        signature(Wildcard, 2) 
        signature(null, 1) 
        signature( 
            group {
                - "Start string .*"
                - 12
            } 
        )
        signature(Type("java.util.List")) 
        signature(1.0 withType "java.lang.Float") 
    }
}

Functions

Since each Op is interpreted by Codyze as one query for function calls to the backend, it would be helpful to have templates for Ops that find calls to the same function but with different filters. This can be achieved with functions in Coko. The parameters of these functions in Coko can be used to pass the specific values to the filter.

An example for an Op template for the function java.util.List.add
fun add(
    element: Any, 
    index: Any? 
) = op {
    "java.util.List.add" {
        signature(element)
        signature(index withType "int", element) 
    }
}

If the reference to a Coko function is used for order rules, all parameters must have a nullable type. Coko invokes them with dummy arguments and uses internal functions to query for all calls to the modelled function regardless of the specified signatures.

Interfaces

A goal of Coko is to make rules more reusable. Aside from specific rules of an API, there might be some general policies that should be followed for all API that provide similar functionality. An example would be that all actions executed on a database should be logged. Instead of writing the corresponding rule for all combinations of database and logging APIs, one might want to combine this into one reusable rule.

This reusability is achieved through interfaces. In Coko interfaces and their functions describe the functionalities that a group of APIs has in common. Rules can thus be written on a more conceptual level.

When Coko encounters rules using a Coko interface as a parameter, it will use one of the available classes implementing the interface as argument to evaluate the rule. Currently, it uses the first class it finds, however, we will implement an algorithm in the future which will try to find the implementation that fits the best for the analyzed code.

Example of a interface and its implementations in Coko
interface Logging {
    fun log(message: Any?, vararg args: Any?): Op
}

class JavaLogging: Logging {
    override fun log(message: Any?, vararg args: Any?): Op =
        op {
            "java.util.logging.Logger.info" {
                signature {
                    group {
                        - message
                        args.forEach { - it }
                    }
                }
            } 
        }
}

class PythonLogging: Logging {
    override fun log(message: Any?, vararg args: Any?): Op =
        op {
            "logging.info" { 
                signature(args) { 
                    - message 
                } 
            }
        }
}

Classes

Classes in Coko model the actual components of an API. They can implement interfaces or just be normal classes. With classes, API functions can be grouped

For APIs written in object-oriented languages it might be sensible to create a Coko class for each class in the API.


  1. In a real example this would be redundant because this case is already covered by the first filter with Wildcard