In this section:

Introduction

In most cases, the commands and node properties shipped with RuleWizard are sufficient to help you build coding patterns you want to identify. There may be cases, however, when the predefined components are unable to represent the exact pattern you want to identify or express the exact action you want performed when a given pattern is identified. In these cases, you can use a script to customize rules.

You can add pieces of code to the rule definition to provide a highly customizable definition of the pattern enforced by the rule. Unlike predefined blocks, scripts invoked from inside the rules can access the current context of the rule (such as visited function, variable, scope, etc.). You can design a script to perform any specific function.

Scripting components are referred to as Methods in RuleWizard. Methods can be added to your rule to act as a node within the rule or to act on the output of another node. Methods can be written in CPython, Java, Jython, and JavaScript. They can take zero, one, or two arguments. The first argument, if provided, is always a value passed from the node to which it is attached. The second argument can provide contextual information about the file that you are testing.

Adding Scripted Components

After creating a rule in the RuleWizard GUI, you can add scripted components to the rule definition. Parasoft static analysis will execute the script when the associated rule component is traversed. Scripts can be added to rule definitions as an output component or as a separate node.

When adding scripts as an output component, the scripts can issue a violation method if the rule is not adhered to.

When added as a separate node, scripts can control the behavior of the entire rule by returning a value of 1 (one) to indicate a violation, or by returning a value of 0 (zero) to indicate conformance to the verified rule.

  1. Right-click on a node and choose one of the following: 
    • Create Output> Method. This will add a script as a rule node.
    • Create Method> Boolean or Node. This will add a script as an output component.  

      Boolean Methods will be added as a child component that returns a Boolean value. The method will take a node argument corresponding to its parent. For example, if a Boolean Method Component is a child of a String Component, the node argument passed to the method is a string. This has similar functionality to the Output Component when it is configured for a Method.

      Node Methods return a node that is passed on to its children. The top portion of the node will be the name of the method set for that component, and the bottom portion will list the types that the component acts on.
       
  2. Choose a language from the Language drop-down menu and define the script to be implemented in the Implementation box. You can use an existing file as the source of code for your method or you can create the method within RuleWizard.
    • To use an existing file, enable File option and browse for the script file.
    • To write the Method in RuleWizard, enable the Text option and enter your code in the text field.
  3. Choose the appropriate argument from the Method drop-down list. This list will be composed of any definitions contained in your script. Since a script can contain multiple arguments you can select the one that you want to use in this method.
  4. Click OK. The method is now ready to be executed with your rule.

You can add as many methods to your rule as you wish. Each time a Method node is processed, the specified code will be invoked to either provide rule output or process information from the current rule context.

If the method is used as an output component, the rule will be considered as valid and marked as yellow once the code is provided in the output component. RuleWizard assumes that the code will provide some output for the rule. 

Checking the Script

To check that the specified script is valid and runnable (or to add method entries to the Method box), right-click the File or Text text field (click whichever one you used to specify your script) and choose Evaluate from the shortcut menu.

Method Requirements

A rule can have any number of nodes using scripts. The methods should meet the following conditions.

Arguments

Methods should have zero, one, or two arguments. The first argument, if provided, is a NodeProvider type. The second argument is a RuleContext type.

Example (Jython method signatures):

def function(node, context):

def function(node):

def function(): 

As the rule is traversed, it is matched to the parse tree of the source file. The node argument above is a NodeProvider object, which is a wrapper object around a parse tree node. You can access various properties of the parse tree node in this way.

The context argument above is a RuleContext object. Here you can store data which will be available to other rules, or call various utility functions, such as executing another rule or sending a message to standard output.

Method namesThere is no restriction on method names. 
Boolean method return valuesBoolean methods should either return 1 to indicate a violation or 0 to indicate conformance to the condition being verified.
Method implementationMethod implementation must be valid CPython, Jython, JavaScript, or Java code (indentation must be correct). If an error is detected in an output method, the output symbol will become red to indicate that the code is incorrect.
Hand-written scriptsIf the script was entered in the text box of the Customize Method dialog, the rule must be saved every time the script is modified.

Language-specifc Information

Java

When you specify a Java class in any of the scripting Class fields, you must specify a compiled class that is on your classpath.

If the class you are using is part of a package, you need to specify the complete package name as well as the class name (for example, java.lang.String)

JavaScript

JavaScript Methods can call Java classes and methods that are on the classpath.

The RuleWizard JavaScript emulation is based on FESI. For details on FESI, visit http://home.worldcom.ch/~jmlugrin/fesi/

Python/Jython

RuleWizard supports the Jython implementation of Python. Jython is an implementation of Python that is integrated with Java. For information on Jython, including information on how to write Jython and also how to invoke Java classes from inside Jython, visit http://www.jython.org.

You need to ensure that the directory which contains the script is on the Python path.

Advanced Scripting for C/C++

RuleWizard supports additional scripting options for C and C++.

Primary API Modules

You can use RuleWizard’s C/C++ API for Python scripting. Before you start working with this API, we recommend that you become familiar with the following three API modules:

NodeProvider

This module functions as a wrapper for the node from which the Python script is invoked. Depending on the node, various sets of properties are available.

RuleContext

Provides access to the current rule context. For example, RuleContext allows the following:

  • Obtain content of a collector as a list using context.getList (“A”) where “A” is the label of the collector.
  • Generate an error message by calling context.report (“Violation detected”) where “Violation detected” will appear in the message body when the violation is reported.

EnforcerContext

Allows for the use of Cross-Rules where one C++ or C++ Text rule can invoke another C++ Text rule from within Python scripts.

Cross-rules

Cross-rules enable you to call existing text rules from within a Python script embedded in another C++ or Text rule. This allows you to combine multiple rules with some basic functionality into a single rule that enforces more complex patterns.

To create a Cross-rule, add a Python script as described in #Adding Scripted Components and insert the code to invoke the rule in the Text box of the Customize Method dialog box.

Invoke rules using the RuleEnforcerContext instance (accessible via the second argu­ment of a Python method) by calling the executeRule() method of the enforcer. The executeR­ule() method must pass two arguments:

  • The name of the rule to invoke.
  • The node passed to the Python method calling the rule.

The executeRule() method returns 1 (one) if the invoked text rule detected any violations, and 0 (zero) otherwise. Use this return value to alter the flow of control in your Python method.

For example, you can enter the following code as a cross-rule:

def somePythonMethod(node, context): 
# ... 
# invoke here the "lower lever" text rule: 
parent = context.getParentContext() 
result = parent.executeRule("./my_text.rule", node) 
# my_text.rule was executed and the result 
# specifies whether any violation was detected 
if 1 == result: 
# my_text.rule reported violation 
else: 
# my_text.rule did not detect any violations 
# ...

Advanced Scripting for Java

Jython is an implementation of Python that is integrated with Java. RuleWizard’s API for Java allows you to add Jython code to the rule definition to ensure maximum flexibility in customizing the patterns enforced by the rules. You can change the rule pattern to specify any number of violations, as well as collect, process and save data.

Using RuleWizard’s API for Java gives you numerous possibilities, which include:

  • Adding 1 to the parameter number shown in the violation message to display parameters starting with 1 (by default, the count starts at 0).
  • Reporting an error for variables that have been collected by collectors and differ only by one "_" character.
  • Operating on collectors to detect variables that are not declared in alphabetical order.
  • Writing messages to the system console so that the CI server can show the build status before the build finishes.
  • Using data collected with other tools. For example, an Excel file can be exported to CVS and used when the rule is executed. The data collected in the process can be used to generate a script to exclude some results.
  • Saving data to a text file so that it can be further processed with other software.

Definition

The definition of your Jython method should contain two arguments:

def check(node, context): 

The node argument represents the node where the Jython script runs. The context argument represents the RuleWizard framework that can be used to report violations.

Any name can be used for the method name.

Node

The following node arguments are supported.

NodeDescription
node.getFileName()

Returns the source file name

node.getFilePath()

Returns the path to the file

node.getFileContent()

Returns the content of the file

node.getNodeContent()

Returns the content of the node

node.getStartColumn()

Returns the start column of the node in the source file

node.getEndColumn()

Returns the end column of the node in the course file

node.getStartLine()

Returns the start line of the node in the source file

node.getEndLine()

Returns the end line of the node in the source file

Context

The following context arguments are supported.

ContextDescription
context.report(String message)

Reports a triggered by the current node

context.get(String)

Gets a value stored in the context

context.put(String,String)

Stores a value in the context. Can be used to transfer information between scripts in a rule

context.getKeys()

Gets a list of keys stored in the context

context.getCollection(String collectorLabel)

Allows you to operate on collected elements in the RuleWizard collector. Returns a collection of nodes

Example Usage

def check(node, context):
	print "Number of collected elements:" + str(len(context.getCollection("A")))
	for x in context.getCollection("A"):
		print x
		print "---"
	return 1

Example Scenario

The print method writes messages to the Jtest console. In the example below, an occurrence of the node will print all the methods from this node to the console and report a violation.

def check (node, context):
	print "filename: " + node.getFileName()
	print "content file: " + node.getFileContent()
	print "node content: " + node.getNodeContent()
	print "start column: " + str(node.getStartColumn())
	print "start line: " + str(node.getStartLine())
	print "end column: " + str(node.getEndColumn())
	print "end line: " + str(node.getEndline())
	context.report("Line " +str(node.getStartLine()) + " from " + node.getFileName() + " contains 'if' statement")
	return 1
  • No labels