/***************************************************************************
* Copyright (C) 2006-2011 by Fabrizio Montesi <famontesi@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License as *
* published by the Free Software Foundation; either version 2 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* For details about the authors of this software, see the AUTHORS file. *
***************************************************************************/
package jolie.lang.parse;
import java.util.Collection;
import jolie.lang.parse.context.ParsingContext;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;
import jolie.lang.Constants.ExecutionMode;
import jolie.lang.Constants.OperandType;
import jolie.lang.Constants.OperationType;
import jolie.lang.parse.CorrelationFunctionInfo.CorrelationPairInfo;
import jolie.lang.parse.ast.AddAssignStatement;
import jolie.lang.parse.ast.expression.AndConditionNode;
import jolie.lang.parse.ast.AssignStatement;
import jolie.lang.parse.ast.DocumentationComment;
import jolie.lang.parse.ast.CompareConditionNode;
import jolie.lang.parse.ast.CompensateStatement;
import jolie.lang.parse.ast.expression.ConstantIntegerExpression;
import jolie.lang.parse.ast.expression.ConstantDoubleExpression;
import jolie.lang.parse.ast.expression.ConstantStringExpression;
import jolie.lang.parse.ast.CorrelationSetInfo;
import jolie.lang.parse.ast.CorrelationSetInfo.CorrelationAliasInfo;
import jolie.lang.parse.ast.CurrentHandlerStatement;
import jolie.lang.parse.ast.DeepCopyStatement;
import jolie.lang.parse.ast.EmbeddedServiceNode;
import jolie.lang.parse.ast.ExecutionInfo;
import jolie.lang.parse.ast.ExitStatement;
import jolie.lang.parse.ast.ForEachStatement;
import jolie.lang.parse.ast.ForStatement;
import jolie.lang.parse.ast.IfStatement;
import jolie.lang.parse.ast.InstallFixedVariableExpressionNode;
import jolie.lang.parse.ast.InstallStatement;
import jolie.lang.parse.ast.expression.IsTypeExpressionNode;
import jolie.lang.parse.ast.LinkInStatement;
import jolie.lang.parse.ast.LinkOutStatement;
import jolie.lang.parse.ast.NDChoiceStatement;
import jolie.lang.parse.ast.expression.NotExpressionNode;
import jolie.lang.parse.ast.NotificationOperationStatement;
import jolie.lang.parse.ast.NullProcessStatement;
import jolie.lang.parse.ast.OLSyntaxNode;
import jolie.lang.parse.ast.OneWayOperationDeclaration;
import jolie.lang.parse.ast.OneWayOperationStatement;
import jolie.lang.parse.ast.OperationDeclaration;
import jolie.lang.parse.ast.expression.OrConditionNode;
import jolie.lang.parse.ast.OutputPortInfo;
import jolie.lang.parse.ast.ParallelStatement;
import jolie.lang.parse.ast.PointerStatement;
import jolie.lang.parse.ast.PostDecrementStatement;
import jolie.lang.parse.ast.PostIncrementStatement;
import jolie.lang.parse.ast.PreDecrementStatement;
import jolie.lang.parse.ast.PreIncrementStatement;
import jolie.lang.parse.ast.expression.ProductExpressionNode;
import jolie.lang.parse.ast.Program;
import jolie.lang.parse.ast.RequestResponseOperationDeclaration;
import jolie.lang.parse.ast.RequestResponseOperationStatement;
import jolie.lang.parse.ast.RunStatement;
import jolie.lang.parse.ast.Scope;
import jolie.lang.parse.ast.SequenceStatement;
import jolie.lang.parse.ast.InputPortInfo;
import jolie.lang.parse.ast.SolicitResponseOperationStatement;
import jolie.lang.parse.ast.DefinitionCallStatement;
import jolie.lang.parse.ast.DefinitionNode;
import jolie.lang.parse.ast.DivideAssignStatement;
import jolie.lang.parse.ast.expression.FreshValueExpressionNode;
import jolie.lang.parse.ast.InterfaceDefinition;
import jolie.lang.parse.ast.InterfaceExtenderDefinition;
import jolie.lang.parse.ast.SubtractAssignStatement;
import jolie.lang.parse.ast.MultiplyAssignStatement;
import jolie.lang.parse.ast.SpawnStatement;
import jolie.lang.parse.ast.expression.SumExpressionNode;
import jolie.lang.parse.ast.SynchronizedStatement;
import jolie.lang.parse.ast.ThrowStatement;
import jolie.lang.parse.ast.TypeCastExpressionNode;
import jolie.lang.parse.ast.UndefStatement;
import jolie.lang.parse.ast.ValueVectorSizeExpressionNode;
import jolie.lang.parse.ast.expression.VariableExpressionNode;
import jolie.lang.parse.ast.VariablePathNode;
import jolie.lang.parse.ast.WhileStatement;
import jolie.lang.parse.ast.courier.CourierChoiceStatement;
import jolie.lang.parse.ast.courier.CourierDefinitionNode;
import jolie.lang.parse.ast.courier.NotificationForwardStatement;
import jolie.lang.parse.ast.courier.SolicitResponseForwardStatement;
import jolie.lang.parse.ast.expression.ConstantBoolExpression;
import jolie.lang.parse.ast.expression.ConstantByteExpression;
import jolie.lang.parse.ast.expression.ConstantInteger16Expression;
import jolie.lang.parse.ast.expression.ConstantLongExpression;
import jolie.lang.parse.ast.expression.ConstantUInteger16Expression;
import jolie.lang.parse.ast.expression.ConstantUInteger32Expression;
import jolie.lang.parse.ast.expression.ConstantUInteger64Expression;
import jolie.lang.parse.ast.expression.InstanceOfExpressionNode;
import jolie.lang.parse.ast.types.TypeDefinition;
import jolie.lang.parse.ast.types.TypeDefinitionLink;
import jolie.lang.parse.ast.types.TypeInlineDefinition;
import jolie.lang.parse.context.URIParsingContext;
import jolie.util.ArrayListMultiMap;
import jolie.util.MultiMap;
import jolie.util.Pair;
/**
* Checks the well-formedness and validity of a JOLIE program.
* @see Program
* @author Fabrizio Montesi
*/
public class SemanticVerifier implements OLVisitor
{
public static class Configuration {
private boolean checkForMain = true;
public void setCheckForMain( boolean checkForMain )
{
this.checkForMain = checkForMain;
}
public boolean checkForMain()
{
return checkForMain;
}
}
private final Program program;
private boolean valid = true;
private final Configuration configuration;
private ExecutionInfo executionInfo = new ExecutionInfo( URIParsingContext.DEFAULT, ExecutionMode.SINGLE );
private final Map< String, InputPortInfo > inputPorts = new HashMap< String, InputPortInfo >();
private final Map< String, OutputPortInfo > outputPorts = new HashMap< String, OutputPortInfo >();
private final Set< String > subroutineNames = new HashSet< String > ();
private final Map< String, OneWayOperationDeclaration > oneWayOperations =
new HashMap< String, OneWayOperationDeclaration >();
private final Map< String, RequestResponseOperationDeclaration > requestResponseOperations =
new HashMap< String, RequestResponseOperationDeclaration >();
private final Map< TypeDefinition, List< TypeDefinition > > typesToBeEqual = new HashMap< TypeDefinition, List< TypeDefinition > >();
private final Map< OneWayOperationDeclaration, List< OneWayOperationDeclaration > > owToBeEqual =
new HashMap< OneWayOperationDeclaration, List< OneWayOperationDeclaration > >();
private final Map< RequestResponseOperationDeclaration, List< RequestResponseOperationDeclaration > > rrToBeEqual =
new HashMap< RequestResponseOperationDeclaration, List< RequestResponseOperationDeclaration > >();
private final List< CorrelationSetInfo > correlationSets = new LinkedList< CorrelationSetInfo >();
private boolean insideInputPort = false;
private boolean insideInit = false;
private boolean mainDefined = false;
private CorrelationFunctionInfo correlationFunctionInfo = new CorrelationFunctionInfo();
private final MultiMap< String, String > inputTypeNameMap =
new ArrayListMultiMap< String, String >(); // Maps type names to the input operations that use them
private ExecutionMode executionMode = ExecutionMode.SINGLE;
private static final Logger logger = Logger.getLogger( "JOLIE" );
private final Map< String, TypeDefinition > definedTypes;
private final List< TypeDefinitionLink > definedTypeLinks = new LinkedList< TypeDefinitionLink >();
//private TypeDefinition rootType; // the type representing the whole session state
private final Map< String, Boolean > isConstantMap = new HashMap< String, Boolean >();
private OperationType insideCourierOperationType = null;
public SemanticVerifier( Program program, Configuration configuration )
{
this.program = program;
this.definedTypes = OLParser.createTypeDeclarationMap( program.context() );
this.configuration = configuration;
/*rootType = new TypeInlineDefinition(
new ParsingContext(),
"#RootType",
NativeType.VOID,
jolie.lang.Constants.RANGE_ONE_TO_ONE
);*/
}
public SemanticVerifier( Program program )
{
this( program, new Configuration() );
}
public CorrelationFunctionInfo correlationFunctionInfo()
{
return correlationFunctionInfo;
}
public ExecutionMode executionMode()
{
return executionMode;
}
private void encounteredAssignment( String varName )
{
if ( isConstantMap.containsKey( varName ) ) {
isConstantMap.put( varName, false );
} else {
isConstantMap.put( varName, true );
}
}
private void addTypeEqualnessCheck( TypeDefinition key, TypeDefinition type )
{
List< TypeDefinition > toBeEqualList = typesToBeEqual.get( key );
if ( toBeEqualList == null ) {
toBeEqualList = new LinkedList< TypeDefinition >();
typesToBeEqual.put( key, toBeEqualList );
}
toBeEqualList.add( type );
}
private void addOneWayEqualnessCheck( OneWayOperationDeclaration key, OneWayOperationDeclaration oneWay )
{
List< OneWayOperationDeclaration > toBeEqualList = owToBeEqual.get( key );
if ( toBeEqualList == null ) {
toBeEqualList = new LinkedList< OneWayOperationDeclaration >();
owToBeEqual.put( key, toBeEqualList );
}
toBeEqualList.add( oneWay );
}
private void addRequestResponseEqualnessCheck( RequestResponseOperationDeclaration key, RequestResponseOperationDeclaration requestResponse )
{
List< RequestResponseOperationDeclaration > toBeEqualList = rrToBeEqual.get( key );
if ( toBeEqualList == null ) {
toBeEqualList = new LinkedList< RequestResponseOperationDeclaration >();
rrToBeEqual.put( key, toBeEqualList );
}
toBeEqualList.add( requestResponse );
}
private void encounteredAssignment( VariablePathNode path )
{
encounteredAssignment( ((ConstantStringExpression)path.path().get( 0 ).key()).value() );
}
public Map< String, Boolean > isConstantMap()
{
return isConstantMap;
}
private void warning( OLSyntaxNode node, String message )
{
if ( node == null ) {
logger.warning( message );
} else {
logger.warning( node.context().sourceName() + ":" + node.context().line() + ": " + message );
}
}
private void error( OLSyntaxNode node, String message )
{
valid = false;
if ( node != null ) {
ParsingContext context = node.context();
logger.severe( context.sourceName() + ":" + context.line() + ": " + message );
} else {
logger.severe( message );
}
}
private void resolveLazyLinks()
{
for( TypeDefinitionLink l : definedTypeLinks ) {
l.setLinkedType( definedTypes.get( l.linkedTypeName() ) );
if ( l.linkedType() == null ) {
error( l, "type " + l.id() + "points to an undefined type (" + l.linkedTypeName() + ")" );
}
}
}
private void checkToBeEqualTypes()
{
for( Entry< TypeDefinition, List< TypeDefinition > > entry : typesToBeEqual.entrySet() ) {
for( TypeDefinition type : entry.getValue() ) {
if ( entry.getKey().isEquivalentTo( type ) == false ) {
error( type, "type " + type.id() + " has already been defined with a different structure" );
}
}
}
for( Entry< OneWayOperationDeclaration, List< OneWayOperationDeclaration > > entry : owToBeEqual.entrySet() ) {
for( OneWayOperationDeclaration ow : entry.getValue() ) {
checkEqualness( entry.getKey(), ow );
}
}
for( Entry< RequestResponseOperationDeclaration, List< RequestResponseOperationDeclaration > > entry : rrToBeEqual.entrySet() ) {
for( RequestResponseOperationDeclaration rr : entry.getValue() ) {
checkEqualness( entry.getKey(), rr );
}
}
}
private void checkCorrelationSets()
{
Collection< String > operations;
Set< String > correlatingOperations = new HashSet< String >();
Set< String > currCorrelatingOperations = new HashSet< String >();
for( CorrelationSetInfo cset : correlationSets ) {
correlationFunctionInfo.correlationSets().add( cset );
currCorrelatingOperations.clear();
for( CorrelationSetInfo.CorrelationVariableInfo csetVar : cset.variables() ) {
for( CorrelationAliasInfo alias : csetVar.aliases() ) {
checkCorrelationAlias( alias );
operations = inputTypeNameMap.get( alias.guardName() );
for( String operationName : operations ) {
currCorrelatingOperations.add( operationName );
correlationFunctionInfo.putCorrelationPair(
operationName,
new CorrelationPairInfo(
csetVar.correlationVariablePath(),
alias.variablePath()
)
);
}
}
}
for( String operationName : currCorrelatingOperations ) {
if ( correlatingOperations.contains( operationName ) ) {
error( cset, "Operation " + operationName +
" is specified on more than one correlation set. Each operation can correlate using only one correlation set."
);
} else {
correlatingOperations.add( operationName );
correlationFunctionInfo.operationCorrelationSetMap().put( operationName, cset );
correlationFunctionInfo.correlationSetOperations().put( cset, operationName );
}
}
}
Collection< CorrelationPairInfo > pairs;
for( Map.Entry< String, CorrelationSetInfo > entry : correlationFunctionInfo.operationCorrelationSetMap().entrySet() ) {
pairs = correlationFunctionInfo.getOperationCorrelationPairs( entry.getKey() );
if ( pairs.size() != entry.getValue().variables().size() ) {
error( entry.getValue(), "Operation " + entry.getKey() +
" has not an alias specified for every variable in the correlation set."
);
}
}
}
private void checkCorrelationAlias( CorrelationAliasInfo alias )
{
TypeDefinition type = definedTypes.get( alias.guardName() );
if ( type == null ) {
error( alias.variablePath(), "type " + alias.guardName() + " is undefined" );
}
if ( type.containsPath( alias.variablePath() ) == false ) {
error( alias.variablePath(), "type " + alias.guardName() + " does not contain the specified path" );
}
}
public boolean validate()
{
program.accept( this );
resolveLazyLinks();
checkToBeEqualTypes();
checkCorrelationSets();
if ( configuration.checkForMain && mainDefined == false ) {
error( null, "Main procedure not defined" );
}
if ( !valid ) {
logger.severe( "Aborting: input file semantically invalid." );
return false;
}
return valid;
}
private boolean isTopLevelType = true;
public void visit( TypeInlineDefinition n )
{
checkCardinality( n );
boolean backupRootType = isTopLevelType;
if ( isTopLevelType ) {
// Check if the type has already been defined with a different structure
TypeDefinition type = definedTypes.get( n.id() );
if ( type != null ) {
addTypeEqualnessCheck( type, n );
}
}
isTopLevelType = false;
if ( n.hasSubTypes() ) {
for( Entry< String, TypeDefinition > entry : n.subTypes() ) {
entry.getValue().accept( this );
}
}
isTopLevelType = backupRootType;
if ( isTopLevelType ) {
definedTypes.put( n.id(), n );
}
}
public void visit( TypeDefinitionLink n )
{
checkCardinality( n );
if ( isTopLevelType ) {
// Check if the type has already been defined with a different structure
TypeDefinition type = definedTypes.get( n.id() );
if ( type != null ) {
addTypeEqualnessCheck( type, n );
}
definedTypes.put( n.id(), n );
}
definedTypeLinks.add( n );
}
private void checkCardinality( TypeDefinition type )
{
if ( type.cardinality().min() < 0 ) {
error( type, "type " + type.id() + " specifies an invalid minimum range value (must be positive)" );
}
if ( type.cardinality().max() < 0 ) {
error( type, "type " + type.id() + " specifies an invalid maximum range value (must be positive)" );
}
}
public void visit( SpawnStatement n )
{
n.body().accept( this );
}
public void visit( DocumentationComment n )
{}
public void visit( Program n )
{
for( OLSyntaxNode node : n.children() ) {
node.accept( this );
}
}
public void visit( VariablePathNode n )
{
if ( insideInit && n.isCSet() ) {
error( n, "Correlation variable access is forbidden in init procedures" );
}
if ( n.isCSet() && !n.isStatic() ) {
error( n, "Correlation paths must be statically defined" );
}
if ( !(n.path().get( 0 ).key() instanceof ConstantStringExpression) ) {
if ( n.isGlobal() ) {
error( n, "the global keyword in paths must be followed by an identifier" );
} else if ( n.isCSet() ) {
error( n, "the csets keyword in paths must be followed by an identifier" );
} else {
error( n, "paths must start with an identifier" );
}
}
}
public void visit( InputPortInfo n )
{
if ( inputPorts.get( n.id() ) != null ) {
error( n, "input port " + n.id() + " has been already defined" );
}
inputPorts.put( n.id(), n );
insideInputPort = true;
Set< String > opSet = new HashSet< String >();
for( OperationDeclaration op : n.operations() ) {
if ( opSet.contains( op.id() ) ) {
error( n, "input port " + n.id() + " declares operation " + op.id() + " multiple times" );
} else {
opSet.add( op.id() );
op.accept( this );
}
}
OutputPortInfo outputPort;
for( InputPortInfo.AggregationItemInfo item : n.aggregationList() ) {
for( String portName : item.outputPortList() ) {
outputPort = outputPorts.get( portName );
if ( outputPort == null ) {
error( n, "input port " + n.id() + " aggregates an undefined output port (" + portName + ")" );
}/* else {
for( OperationDeclaration op : outputPort.operations() ) {
if ( opSet.contains( op.id() ) ) {
error( n, "input port " + n.id() + " declares duplicate operation " + op.id() + " from aggregated output port " + outputPort.id() );
} else {
opSet.add( op.id() );
}
}
}*/
}
}
insideInputPort = false;
}
public void visit( OutputPortInfo n )
{
if ( outputPorts.get( n.id() ) != null )
error( n, "output port " + n.id() + " has been already defined" );
outputPorts.put( n.id(), n );
encounteredAssignment( n.id() );
for( OperationDeclaration op : n.operations() ) {
op.accept( this );
}
}
public void visit( OneWayOperationDeclaration n )
{
if ( definedTypes.get( n.requestType().id() ) == null ) {
error( n, "unknown type: " + n.requestType().id() + " for operation " + n.id() );
}
if ( insideInputPort ) { // Input operation
if ( oneWayOperations.containsKey( n.id() ) ) {
OneWayOperationDeclaration other = oneWayOperations.get( n.id() );
addOneWayEqualnessCheck( n, other );
} else {
oneWayOperations.put( n.id(), n );
inputTypeNameMap.put( n.requestType().id(), n.id() );
}
}
}
public void visit( RequestResponseOperationDeclaration n )
{
if ( definedTypes.get( n.requestType().id() ) == null ) {
error( n, "unknown type: " + n.requestType().id() + " for operation " + n.id() );
}
if ( definedTypes.get( n.responseType().id() ) == null ) {
error( n, "unknown type: " + n.responseType().id() + " for operation " + n.id() );
}
for( Entry< String, TypeDefinition > fault : n.faults().entrySet() ) {
if ( definedTypes.containsKey( fault.getValue().id() ) == false ) {
error( n, "unknown type for fault " + fault.getKey() );
}
}
if ( insideInputPort ) { // Input operation
if ( requestResponseOperations.containsKey( n.id() ) ) {
RequestResponseOperationDeclaration other = requestResponseOperations.get( n.id() );
addRequestResponseEqualnessCheck( n, other );
} else {
requestResponseOperations.put( n.id(), n );
inputTypeNameMap.put( n.requestType().id(), n.id() );
}
}
}
private void checkEqualness( OneWayOperationDeclaration n, OneWayOperationDeclaration other )
{
if ( n.requestType().isEquivalentTo( other.requestType() ) == false ) {
error( n, "input operations sharing the same name cannot declare different request types (One-Way operation " + n.id() + ")" );
}
}
private void checkEqualness( RequestResponseOperationDeclaration n, RequestResponseOperationDeclaration other )
{
if ( n.requestType().isEquivalentTo( other.requestType() ) == false ) {
error( n, "input operations sharing the same name cannot declare different request types (Request-Response operation " + n.id() + ")" );
}
if ( n.responseType().isEquivalentTo( other.responseType() ) == false ) {
error( n, "input operations sharing the same name cannot declare different response types (Request-Response operation " + n.id() + ")" );
}
if ( n.faults().size() != other.faults().size() ) {
error( n, "input operations sharing the same name cannot declared different fault types (Request-Response operation " + n.id() );
}
for( Entry< String, TypeDefinition > fault : n.faults().entrySet() ) {
if ( fault.getValue() != null ) {
if ( !other.faults().containsKey( fault.getKey() ) || !other.faults().get( fault.getKey() ).isEquivalentTo( fault.getValue() ) ) {
error( n, "input operations sharing the same name cannot declared different fault types (Request-Response operation " + n.id() );
}
}
}
}
public void visit( DefinitionNode n )
{
if ( subroutineNames.contains( n.id() ) ) {
error( n, "Procedure " + n.id() + " uses an already defined identifier" );
} else {
subroutineNames.add( n.id() );
}
if ( "main".equals( n.id() ) ) {
mainDefined = true;
if ( executionInfo.mode() != ExecutionMode.SINGLE ) {
if ( ( n.body() instanceof NDChoiceStatement
|| n.body() instanceof RequestResponseOperationStatement
|| n.body() instanceof OneWayOperationStatement
) == false
) {
// The main body is not an input
if ( n.body() instanceof SequenceStatement ) {
OLSyntaxNode first = ((SequenceStatement)n.body()).children().get( 0 );
if ( (first instanceof RequestResponseOperationStatement
|| first instanceof OneWayOperationStatement) == false
) {
// The main body is not even a sequence starting with an input
error( n.body(),
"The first statement of the main procedure must be an input if the execution mode is not single"
);
}
} else {
// The main body is not even a sequence
error( n.body(),
"The first statement of the main procedure must be an input if the execution mode is not single"
);
}
}
}
}
if ( n.id().equals( "init" ) ) {
insideInit = true;
}
n.body().accept( this );
insideInit = false;
}
public void visit( ParallelStatement stm )
{
for( OLSyntaxNode node : stm.children() ) {
node.accept( this );
}
}
public void visit( SequenceStatement stm )
{
for( OLSyntaxNode node : stm.children() ) {
node.accept( this );
}
}
public void visit( NDChoiceStatement stm )
{
Set< String > operations = new HashSet< String >();
String name = null;
for( Pair< OLSyntaxNode, OLSyntaxNode > pair : stm.children() ) {
if ( pair.key() instanceof OneWayOperationStatement ) {
name = ((OneWayOperationStatement)pair.key()).id();
} else if ( pair.key() instanceof RequestResponseOperationStatement ) {
name = ((RequestResponseOperationStatement)pair.key()).id();
} else {
error( pair.key(), "Input choices can contain only One-Way or Request-Response guards" );
}
if ( operations.contains( name ) ) {
error( pair.key(), "Input choices can not have duplicate input guards (input statement for operation " + name + ")" );
} else {
operations.add( name );
}
pair.key().accept( this );
pair.value().accept( this );
}
}
public void visit( NotificationOperationStatement n )
{
OutputPortInfo p = outputPorts.get( n.outputPortId() );
if ( p == null ) {
error( n, n.outputPortId() + " is not a valid output port" );
} else {
OperationDeclaration decl = p.operationsMap().get( n.id() );
if ( decl == null )
error( n, "Operation " + n.id() + " has not been declared in output port type " + p.id() );
else if ( !( decl instanceof OneWayOperationDeclaration ) )
error( n, "Operation " + n.id() + " is not a valid one-way operation in output port " + p.id() );
}
}
public void visit( SolicitResponseOperationStatement n )
{
if ( n.inputVarPath() != null ) {
encounteredAssignment( n.inputVarPath() );
}
OutputPortInfo p = outputPorts.get( n.outputPortId() );
if ( p == null ) {
error( n, n.outputPortId() + " is not a valid output port" );
} else {
OperationDeclaration decl = p.operationsMap().get( n.id() );
if ( decl == null ) {
error( n, "Operation " + n.id() + " has not been declared in output port " + p.id() );
} else if ( !(decl instanceof RequestResponseOperationDeclaration) ) {
error( n, "Operation " + n.id() + " is not a valid request-response operation in output port " + p.id() );
}
}
/*if ( n.inputVarPath() != null && n.inputVarPath().isCSet() ) {
error( n, "Receiving a message in a correlation variable is forbidden" );
}*/
}
public void visit( ThrowStatement n )
{
verify( n.expression() );
}
public void visit( CompensateStatement n ) {}
public void visit( InstallStatement n )
{
for( Pair< String, OLSyntaxNode > pair : n.handlersFunction().pairs() ) {
pair.value().accept( this );
}
}
public void visit( Scope n )
{
n.body().accept( this );
}
public void visit( OneWayOperationStatement n )
{
if ( insideCourierOperationType != null ) {
error( n, "input statements are forbidden inside courier definitions" );
}
verify( n.inputVarPath() );
if ( n.inputVarPath() != null ) {
if ( n.inputVarPath().isCSet() ) {
error( n, "Receiving a message in a correlation variable is forbidden" );
}
encounteredAssignment( n.inputVarPath() );
}
}
public void visit( RequestResponseOperationStatement n )
{
if ( insideCourierOperationType != null ) {
error( n, "input statements are forbidden inside courier definitions" );
}
verify( n.inputVarPath() );
verify( n.process() );
if ( n.inputVarPath() != null ) {
if ( n.inputVarPath().isCSet() ) {
error( n, "Receiving a message in a correlation variable is forbidden" );
}
encounteredAssignment( n.inputVarPath() );
}
}
public void visit( LinkInStatement n ) {}
public void visit( LinkOutStatement n ) {}
public void visit( SynchronizedStatement n )
{
n.body().accept( this );
}
public void visit( AssignStatement n )
{
n.variablePath().accept( this );
encounteredAssignment( n.variablePath() );
n.expression().accept( this );
}
public void visit( InstanceOfExpressionNode n )
{
n.expression().accept( this );
}
public void visit( AddAssignStatement n )
{
encounteredAssignment( n.variablePath() );
n.variablePath().accept( this );
n.expression().accept( this );
}
public void visit( SubtractAssignStatement n )
{
encounteredAssignment( n.variablePath() );
n.variablePath().accept( this );
n.expression().accept( this );
}
public void visit( MultiplyAssignStatement n )
{
encounteredAssignment( n.variablePath() );
n.variablePath().accept( this );
n.expression().accept( this );
}
public void visit( DivideAssignStatement n )
{
encounteredAssignment( n.variablePath() );
n.variablePath().accept( this );
n.expression().accept( this );
}
private void verify( OLSyntaxNode n )
{
if ( n != null ) {
n.accept( this );
}
}
public void visit( PointerStatement n )
{
encounteredAssignment( n.leftPath() );
encounteredAssignment( n.rightPath() );
n.leftPath().accept( this );
n.rightPath().accept( this );
if ( n.rightPath().isCSet() ) {
error( n, "Making an alias to a correlation variable is forbidden" );
}
}
public void visit( DeepCopyStatement n )
{
encounteredAssignment( n.leftPath() );
n.leftPath().accept( this );
n.rightPath().accept( this );
if ( n.leftPath().isCSet() ) {
error( n, "Deep copy on a correlation variable is forbidden" );
}
}
public void visit( IfStatement n )
{
for( Pair< OLSyntaxNode, OLSyntaxNode > choice : n.children() ) {
verify( choice.key() );
verify( choice.value() );
}
verify( n.elseProcess() );
}
public void visit( DefinitionCallStatement n )
{
if ( !subroutineNames.contains( n.id() ) ) {
error( n, "Call to undefined definition: " + n.id() );
}
}
public void visit( WhileStatement n )
{
n.condition().accept( this );
n.body().accept( this );
}
public void visit( OrConditionNode n )
{
for( OLSyntaxNode node : n.children() ) {
node.accept( this );
}
}
public void visit( AndConditionNode n )
{
for( OLSyntaxNode node : n.children() ) {
node.accept( this );
}
}
public void visit( NotExpressionNode n )
{
n.expression().accept( this );
}
public void visit( CompareConditionNode n )
{
n.leftExpression().accept( this );
n.rightExpression().accept( this );
}
public void visit( ConstantIntegerExpression n ) {}
public void visit( ConstantUInteger32Expression n ) {}
public void visit(ConstantByteExpression n) {}
public void visit(ConstantInteger16Expression n) {}
public void visit(ConstantUInteger16Expression n) {}
public void visit(ConstantUInteger64Expression n) {}
public void visit( ConstantDoubleExpression n ) {}
public void visit( ConstantStringExpression n ) {}
public void visit( ConstantLongExpression n ) {}
public void visit( ConstantBoolExpression n ) {}
public void visit( ProductExpressionNode n )
{
for( Pair< OperandType, OLSyntaxNode > pair : n.operands() ) {
pair.value().accept( this );
}
}
public void visit( SumExpressionNode n )
{
for( Pair< OperandType, OLSyntaxNode > pair : n.operands() ) {
pair.value().accept( this );
}
}
public void visit( VariableExpressionNode n )
{
n.variablePath().accept( this );
}
public void visit( InstallFixedVariableExpressionNode n )
{
n.variablePath().accept( this );
}
public void visit( NullProcessStatement n ) {}
public void visit( ExitStatement n ) {}
public void visit( ExecutionInfo n )
{
executionMode = n.mode();
executionInfo = n;
}
public void visit( CorrelationSetInfo n )
{
VariablePathSet pathSet = new VariablePathSet();
VariablePathNode path;
for( CorrelationSetInfo.CorrelationVariableInfo csetVar : n.variables() ) {
path = csetVar.correlationVariablePath();
if ( path.isGlobal() ) {
error( path, "Correlation variables can not be global" );
} else if ( path.isCSet() ) {
error( path, "Correlation variables can not be in the csets structure" );
} else {
if ( path.isStatic() == false ) {
error( path, "correlation variable paths can not make use of dynamic evaluation" );
}
}
if ( pathSet.contains( path ) ) {
error( path, "Duplicate correlation variable" );
} else {
pathSet.add( path );
}
for( CorrelationAliasInfo alias : csetVar.aliases() ) {
if ( alias.variablePath().isGlobal() ) {
error( alias.variablePath(), "Correlation variables can not be global" );
} else if ( path.isCSet() ) {
error( alias.variablePath(), "Correlation variables can not be in the csets structure" );
} else {
if ( alias.variablePath().isStatic() == false ) {
error( alias.variablePath(), "correlation variable path aliases can not make use of dynamic evaluation" );
}
}
}
}
correlationSets.add( n );
/*VariablePathNode varPath;
List< Pair< OLSyntaxNode, OLSyntaxNode > > path;
for( List< VariablePathNode > list : n.variables() ) {
varPath = list.get( 0 );
if ( varPath.isGlobal() ) {
error( list.get( 0 ), "Correlation variables can not be global" );
}
path = varPath.path();
if ( path.size() > 1 ) {
error( varPath, "Correlation variables can not be nested paths" );
} else if ( path.get( 0 ).value() != null ) {
error( varPath, "Correlation variables can not use arrays" );
} else {
correlationSet.add( ((ConstantStringExpression)path.get( 0 ).key()).value() );
}
}*/
}
public void visit( RunStatement n )
{
warning( n, "Run statement is not a stable feature yet." );
}
public void visit( ValueVectorSizeExpressionNode n )
{
n.variablePath().accept( this );
}
public void visit( PreIncrementStatement n )
{
encounteredAssignment( n.variablePath() );
n.variablePath().accept( this );
}
public void visit( PostIncrementStatement n )
{
encounteredAssignment( n.variablePath() );
n.variablePath().accept( this );
}
public void visit( PreDecrementStatement n )
{
encounteredAssignment( n.variablePath() );
n.variablePath().accept( this );
}
public void visit( PostDecrementStatement n )
{
encounteredAssignment( n.variablePath() );
n.variablePath().accept( this );
}
public void visit( UndefStatement n )
{
encounteredAssignment( n.variablePath() );
n.variablePath().accept( this );
if ( n.variablePath().isCSet() ) {
error( n, "Undefining a correlation variable is forbidden" );
}
}
public void visit( ForStatement n )
{
n.init().accept( this );
n.condition().accept( this );
n.post().accept( this );
n.body().accept( this );
}
public void visit( ForEachStatement n )
{
n.keyPath().accept( this );
n.targetPath().accept( this );
n.body().accept( this );
}
public void visit( IsTypeExpressionNode n )
{
n.variablePath().accept( this );
}
public void visit( TypeCastExpressionNode n )
{
n.expression().accept( this );
}
public void visit( EmbeddedServiceNode n )
{}
public void visit( InterfaceExtenderDefinition n ) {}
public void visit( CourierDefinitionNode n )
{
if ( inputPorts.containsKey( n.inputPortName() ) == false ) {
error( n, "undefined input port: " + n.inputPortName() );
}
verify( n.body() );
}
public void visit( CourierChoiceStatement n )
{
for( CourierChoiceStatement.InterfaceOneWayBranch branch : n.interfaceOneWayBranches() ) {
insideCourierOperationType = OperationType.ONE_WAY;
verify( branch.body );
}
for( CourierChoiceStatement.InterfaceRequestResponseBranch branch : n.interfaceRequestResponseBranches() ) {
insideCourierOperationType = OperationType.REQUEST_RESPONSE;
verify( branch.body );
}
for( CourierChoiceStatement.OperationOneWayBranch branch : n.operationOneWayBranches() ) {
insideCourierOperationType = OperationType.ONE_WAY;
verify( branch.body );
}
for( CourierChoiceStatement.OperationRequestResponseBranch branch : n.operationRequestResponseBranches() ) {
insideCourierOperationType = OperationType.REQUEST_RESPONSE;
verify( branch.body );
}
insideCourierOperationType = null;
}
/**
* @todo Check that the output port of the forward statement is right wrt the input port aggregation definition.
*/
public void visit( NotificationForwardStatement n )
{
if ( insideCourierOperationType == null ) {
error( n, "the forward statement may be used only inside a courier definition" );
} else if ( insideCourierOperationType != OperationType.ONE_WAY ) {
error( n, "forward statement is a notification, but is inside a request-response courier definition. Maybe you wanted to specify a solicit-response forward?" );
}
}
/**
* @todo Check that the output port of the forward statement is right wrt the input port aggregation definition.
*/
public void visit( SolicitResponseForwardStatement n )
{
if ( insideCourierOperationType == null ) {
error( n, "the forward statement may be used only inside a courier definition" );
} else if ( insideCourierOperationType != OperationType.REQUEST_RESPONSE ) {
error( n, "forward statement is a solicit-response, but is inside a one-way courier definition. Maybe you wanted to specify a notification forward?" );
}
}
/**
* @todo Must check if it's inside an install function
*/
public void visit( CurrentHandlerStatement n )
{}
public void visit( InterfaceDefinition n )
{}
public void visit( FreshValueExpressionNode n )
{}
}