Search in sources :

Example 1 with ConditionalPostcondition

use of org.checkerframework.framework.util.Contract.ConditionalPostcondition in project checker-framework by typetools.

the class CFAbstractTransfer method processConditionalPostconditions.

/**
 * Add information from the conditional postconditions of a method to the stores after an
 * invocation.
 *
 * @param invocationNode a method call
 * @param methodElement the method being called
 * @param invocationTree the tree for the method call
 * @param thenStore the "then" store; is side-effected by this method
 * @param elseStore the "else" store; is side-effected by this method
 */
protected void processConditionalPostconditions(MethodInvocationNode invocationNode, ExecutableElement methodElement, Tree invocationTree, S thenStore, S elseStore) {
    ContractsFromMethod contractsUtils = analysis.atypeFactory.getContractsFromMethod();
    Set<ConditionalPostcondition> conditionalPostconditions = contractsUtils.getConditionalPostconditions(methodElement);
    processPostconditionsAndConditionalPostconditions(invocationNode, invocationTree, thenStore, elseStore, conditionalPostconditions);
}
Also used : ContractsFromMethod(org.checkerframework.framework.util.ContractsFromMethod) ConditionalPostcondition(org.checkerframework.framework.util.Contract.ConditionalPostcondition)

Example 2 with ConditionalPostcondition

use of org.checkerframework.framework.util.Contract.ConditionalPostcondition in project checker-framework by typetools.

the class CFAbstractTransfer method processPostconditionsAndConditionalPostconditions.

/**
 * Add information from the postconditions and conditional postconditions of a method to the
 * stores after an invocation.
 *
 * @param invocationNode a method call
 * @param invocationTree the tree for the method call
 * @param thenStore the "then" store; is side-effected by this method
 * @param elseStore the "else" store; is side-effected by this method
 * @param postconditions the postconditions
 */
private void processPostconditionsAndConditionalPostconditions(MethodInvocationNode invocationNode, Tree invocationTree, S thenStore, S elseStore, Set<? extends Contract> postconditions) {
    StringToJavaExpression stringToJavaExpr = stringExpr -> StringToJavaExpression.atMethodInvocation(stringExpr, invocationNode, analysis.checker);
    for (Contract p : postconditions) {
        // Viewpoint-adapt to the method use (the call site).
        AnnotationMirror anno = p.viewpointAdaptDependentTypeAnnotation(analysis.atypeFactory, stringToJavaExpr, /*errorTree=*/
        null);
        String expressionString = p.expressionString;
        try {
            JavaExpression je = stringToJavaExpr.toJavaExpression(expressionString);
            // are removed from the store before this method is called.
            if (p.kind == Contract.Kind.CONDITIONALPOSTCONDITION) {
                if (((ConditionalPostcondition) p).resultValue) {
                    thenStore.insertOrRefinePermitNondeterministic(je, anno);
                } else {
                    elseStore.insertOrRefinePermitNondeterministic(je, anno);
                }
            } else {
                thenStore.insertOrRefinePermitNondeterministic(je, anno);
            }
        } catch (JavaExpressionParseException e) {
            // report errors here
            if (e.isFlowParseError()) {
                Object[] args = new Object[e.args.length + 1];
                args[0] = ElementUtils.getSimpleSignature(TreeUtils.elementFromUse(invocationNode.getTree()));
                System.arraycopy(e.args, 0, args, 1, e.args.length);
                analysis.checker.reportError(invocationTree, "flowexpr.parse.error.postcondition", args);
            } else {
                analysis.checker.report(invocationTree, e.getDiagMessage());
            }
        }
    }
}
Also used : NodeUtils(org.checkerframework.dataflow.util.NodeUtils) Arrays(java.util.Arrays) TransferResult(org.checkerframework.dataflow.analysis.TransferResult) Modifier(javax.lang.model.element.Modifier) ForwardTransferFunction(org.checkerframework.dataflow.analysis.ForwardTransferFunction) TypeElement(javax.lang.model.element.TypeElement) ClassSymbol(com.sun.tools.javac.code.Symbol.ClassSymbol) StringConversionNode(org.checkerframework.dataflow.cfg.node.StringConversionNode) GenericAnnotatedTypeFactory(org.checkerframework.framework.type.GenericAnnotatedTypeFactory) Map(java.util.Map) WideningConversionNode(org.checkerframework.dataflow.cfg.node.WideningConversionNode) InternedDistinct(org.checkerframework.checker.interning.qual.InternedDistinct) AnnotatedExecutableType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType) ClassNameNode(org.checkerframework.dataflow.cfg.node.ClassNameNode) TreePath(com.sun.source.util.TreePath) ObjectCreationNode(org.checkerframework.dataflow.cfg.node.ObjectCreationNode) UnderlyingAST(org.checkerframework.dataflow.cfg.UnderlyingAST) AbstractNodeVisitor(org.checkerframework.dataflow.cfg.node.AbstractNodeVisitor) Set(java.util.Set) Element(javax.lang.model.element.Element) Contract(org.checkerframework.framework.util.Contract) TreeUtils(org.checkerframework.javacutil.TreeUtils) CFGMethod(org.checkerframework.dataflow.cfg.UnderlyingAST.CFGMethod) LocalVariableNode(org.checkerframework.dataflow.cfg.node.LocalVariableNode) NarrowingConversionNode(org.checkerframework.dataflow.cfg.node.NarrowingConversionNode) List(java.util.List) LocalVariable(org.checkerframework.dataflow.expression.LocalVariable) AnnotatedDeclaredType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType) EqualToNode(org.checkerframework.dataflow.cfg.node.EqualToNode) AnnotatedTypes(org.checkerframework.framework.util.AnnotatedTypes) CFGLambda(org.checkerframework.dataflow.cfg.UnderlyingAST.CFGLambda) VariableDeclarationNode(org.checkerframework.dataflow.cfg.node.VariableDeclarationNode) RegularTransferResult(org.checkerframework.dataflow.analysis.RegularTransferResult) TernaryExpressionNode(org.checkerframework.dataflow.cfg.node.TernaryExpressionNode) Postcondition(org.checkerframework.framework.util.Contract.Postcondition) MethodTree(com.sun.source.tree.MethodTree) ConditionalPostcondition(org.checkerframework.framework.util.Contract.ConditionalPostcondition) VariableElement(javax.lang.model.element.VariableElement) InstanceOfNode(org.checkerframework.dataflow.cfg.node.InstanceOfNode) HashMap(java.util.HashMap) ThisNode(org.checkerframework.dataflow.cfg.node.ThisNode) ArrayList(java.util.ArrayList) ConditionalTransferResult(org.checkerframework.dataflow.analysis.ConditionalTransferResult) AssignmentNode(org.checkerframework.dataflow.cfg.node.AssignmentNode) CaseNode(org.checkerframework.dataflow.cfg.node.CaseNode) HashSet(java.util.HashSet) LambdaResultExpressionNode(org.checkerframework.dataflow.cfg.node.LambdaResultExpressionNode) Precondition(org.checkerframework.framework.util.Contract.Precondition) FieldAccessNode(org.checkerframework.dataflow.cfg.node.FieldAccessNode) TreePathUtil(org.checkerframework.javacutil.TreePathUtil) SwitchExpressionNode(org.checkerframework.dataflow.cfg.node.SwitchExpressionNode) NotEqualNode(org.checkerframework.dataflow.cfg.node.NotEqualNode) Tree(com.sun.source.tree.Tree) ClassTree(com.sun.source.tree.ClassTree) Nullable(org.checkerframework.checker.nullness.qual.Nullable) FieldAccess(org.checkerframework.dataflow.expression.FieldAccess) TransferInput(org.checkerframework.dataflow.analysis.TransferInput) AnnotatedTypeMirror(org.checkerframework.framework.type.AnnotatedTypeMirror) ArrayAccessNode(org.checkerframework.dataflow.cfg.node.ArrayAccessNode) ConditionalNotNode(org.checkerframework.dataflow.cfg.node.ConditionalNotNode) JavaExpressionParseException(org.checkerframework.framework.util.JavaExpressionParseUtil.JavaExpressionParseException) ElementKind(javax.lang.model.element.ElementKind) ExpressionTree(com.sun.source.tree.ExpressionTree) ExecutableElement(javax.lang.model.element.ExecutableElement) ReturnNode(org.checkerframework.dataflow.cfg.node.ReturnNode) JavaExpression(org.checkerframework.dataflow.expression.JavaExpression) FieldInitialValue(org.checkerframework.framework.flow.CFAbstractAnalysis.FieldInitialValue) MethodInvocationNode(org.checkerframework.dataflow.cfg.node.MethodInvocationNode) AnnotationMirror(javax.lang.model.element.AnnotationMirror) StringToJavaExpression(org.checkerframework.framework.util.StringToJavaExpression) AnnotatedTypeFactory(org.checkerframework.framework.type.AnnotatedTypeFactory) StringConcatenateAssignmentNode(org.checkerframework.dataflow.cfg.node.StringConcatenateAssignmentNode) TypeMirror(javax.lang.model.type.TypeMirror) Collections(java.util.Collections) ElementUtils(org.checkerframework.javacutil.ElementUtils) Node(org.checkerframework.dataflow.cfg.node.Node) ContractsFromMethod(org.checkerframework.framework.util.ContractsFromMethod) AnnotationMirror(javax.lang.model.element.AnnotationMirror) JavaExpression(org.checkerframework.dataflow.expression.JavaExpression) StringToJavaExpression(org.checkerframework.framework.util.StringToJavaExpression) StringToJavaExpression(org.checkerframework.framework.util.StringToJavaExpression) JavaExpressionParseException(org.checkerframework.framework.util.JavaExpressionParseUtil.JavaExpressionParseException) ConditionalPostcondition(org.checkerframework.framework.util.Contract.ConditionalPostcondition) Contract(org.checkerframework.framework.util.Contract)

Example 3 with ConditionalPostcondition

use of org.checkerframework.framework.util.Contract.ConditionalPostcondition in project checker-framework by typetools.

the class BaseTypeVisitor method checkContractsAtMethodDeclaration.

/**
 * Check the contracts written on a method declaration. Ensures that the postconditions hold on
 * exit, and that the contracts are well-formed.
 *
 * @param methodTree the method declaration
 * @param methodElement the method element
 * @param formalParamNames the formal parameter names
 * @param abstractMethod whether the method is abstract
 */
private void checkContractsAtMethodDeclaration(MethodTree methodTree, ExecutableElement methodElement, List<String> formalParamNames, boolean abstractMethod) {
    Set<Contract> contracts = atypeFactory.getContractsFromMethod().getContracts(methodElement);
    if (contracts.isEmpty()) {
        return;
    }
    StringToJavaExpression stringToJavaExpr = stringExpr -> StringToJavaExpression.atMethodBody(stringExpr, methodTree, checker);
    for (Contract contract : contracts) {
        String expressionString = contract.expressionString;
        AnnotationMirror annotation = contract.viewpointAdaptDependentTypeAnnotation(atypeFactory, stringToJavaExpr, methodTree);
        JavaExpression exprJe;
        try {
            exprJe = StringToJavaExpression.atMethodBody(expressionString, methodTree, checker);
        } catch (JavaExpressionParseException e) {
            DiagMessage diagMessage = e.getDiagMessage();
            if (diagMessage.getMessageKey().equals("flowexpr.parse.error")) {
                String s = String.format("'%s' in the %s %s on the declaration of method '%s': ", expressionString, contract.kind.errorKey, contract.contractAnnotation.getAnnotationType().asElement().getSimpleName(), methodTree.getName().toString());
                checker.reportError(methodTree, "flowexpr.parse.error", s + diagMessage.getArgs()[0]);
            } else {
                checker.report(methodTree, e.getDiagMessage());
            }
            continue;
        }
        if (!CFAbstractStore.canInsertJavaExpression(exprJe)) {
            checker.reportError(methodTree, "flowexpr.parse.error", expressionString);
            continue;
        }
        if (!abstractMethod && contract.kind != Contract.Kind.PRECONDITION) {
            switch(contract.kind) {
                case POSTCONDITION:
                    checkPostcondition(methodTree, annotation, exprJe);
                    break;
                case CONDITIONALPOSTCONDITION:
                    checkConditionalPostcondition(methodTree, annotation, exprJe, ((ConditionalPostcondition) contract).resultValue);
                    break;
                default:
                    throw new BugInCF("Impossible: " + contract.kind);
            }
        }
        if (formalParamNames != null && formalParamNames.contains(expressionString)) {
            String locationOfExpression = contract.kind.errorKey + " " + contract.contractAnnotation.getAnnotationType().asElement().getSimpleName() + " on the declaration";
            checker.reportWarning(methodTree, "expression.parameter.name.shadows.field", locationOfExpression, methodTree.getName().toString(), expressionString, expressionString, formalParamNames.indexOf(expressionString) + 1);
        }
        checkParametersAreEffectivelyFinal(methodTree, exprJe);
    }
}
Also used : AnnotationEqualityVisitor(org.checkerframework.framework.ajava.AnnotationEqualityVisitor) CompoundAssignmentTree(com.sun.source.tree.CompoundAssignmentTree) Arrays(java.util.Arrays) TransferResult(org.checkerframework.dataflow.analysis.TransferResult) AnnotatedArrayType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType) Modifier(javax.lang.model.element.Modifier) QualifierHierarchy(org.checkerframework.framework.type.QualifierHierarchy) TypeElement(javax.lang.model.element.TypeElement) ClassSymbol(com.sun.tools.javac.code.Symbol.ClassSymbol) DefaultPrettyPrinter(com.github.javaparser.printer.DefaultPrettyPrinter) GenericAnnotatedTypeFactory(org.checkerframework.framework.type.GenericAnnotatedTypeFactory) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) AssignmentTree(com.sun.source.tree.AssignmentTree) TypeCastTree(com.sun.source.tree.TypeCastTree) Vector(java.util.Vector) LambdaExpressionTree(com.sun.source.tree.LambdaExpressionTree) Map(java.util.Map) InstanceOfTree(com.sun.source.tree.InstanceOfTree) EnumSet(java.util.EnumSet) SideEffectFree(org.checkerframework.dataflow.qual.SideEffectFree) AnnotatedExecutableType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType) ConditionalExpressionTree(com.sun.source.tree.ConditionalExpressionTree) TreePath(com.sun.source.util.TreePath) Pure(org.checkerframework.dataflow.qual.Pure) Set(java.util.Set) AnnotatedWildcardType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType) Element(javax.lang.model.element.Element) MemberSelectTree(com.sun.source.tree.MemberSelectTree) TreeUtils(org.checkerframework.javacutil.TreeUtils) TreeScanner(com.sun.source.util.TreeScanner) ParameterizedExecutableType(org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType) Unused(org.checkerframework.framework.qual.Unused) ThrowTree(com.sun.source.tree.ThrowTree) JCIdent(com.sun.tools.javac.tree.JCTree.JCIdent) AnnotationValue(javax.lang.model.element.AnnotationValue) EnhancedForLoopTree(com.sun.source.tree.EnhancedForLoopTree) TypesUtils(org.checkerframework.javacutil.TypesUtils) AnnotatedPrimitiveType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedPrimitiveType) ReturnTree(com.sun.source.tree.ReturnTree) PurityResult(org.checkerframework.dataflow.util.PurityChecker.PurityResult) ArrayTypeTree(com.sun.source.tree.ArrayTypeTree) ConditionalPostcondition(org.checkerframework.framework.util.Contract.ConditionalPostcondition) UnaryTree(com.sun.source.tree.UnaryTree) CFAbstractValue(org.checkerframework.framework.flow.CFAbstractValue) VariableElement(javax.lang.model.element.VariableElement) VariableTree(com.sun.source.tree.VariableTree) BooleanLiteralNode(org.checkerframework.dataflow.cfg.node.BooleanLiteralNode) ReferenceKind(com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind) TypeParameterTree(com.sun.source.tree.TypeParameterTree) ArrayList(java.util.ArrayList) CompilerMessageKey(org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey) NewClassTree(com.sun.source.tree.NewClassTree) ParameterizedTypeTree(com.sun.source.tree.ParameterizedTypeTree) Precondition(org.checkerframework.framework.util.Contract.Precondition) FindDistinct(org.checkerframework.checker.interning.qual.FindDistinct) SwitchExpressionScanner(org.checkerframework.javacutil.SwitchExpressionScanner) TreeInfo(com.sun.tools.javac.tree.TreeInfo) DeclaredType(javax.lang.model.type.DeclaredType) TreePathUtil(org.checkerframework.javacutil.TreePathUtil) ElementFilter(javax.lang.model.util.ElementFilter) Tree(com.sun.source.tree.Tree) LinkedHashSet(java.util.LinkedHashSet) AnnotatedTypeMirror(org.checkerframework.framework.type.AnnotatedTypeMirror) SourceVisitor(org.checkerframework.framework.source.SourceVisitor) QualifierPolymorphism(org.checkerframework.framework.type.poly.QualifierPolymorphism) FieldInvariants(org.checkerframework.framework.util.FieldInvariants) ExpressionTree(com.sun.source.tree.ExpressionTree) IOException(java.io.IOException) Target(java.lang.annotation.Target) AnnotationMirror(javax.lang.model.element.AnnotationMirror) StringToJavaExpression(org.checkerframework.framework.util.StringToJavaExpression) ParseProblemException(com.github.javaparser.ParseProblemException) DiagMessage(org.checkerframework.framework.source.DiagMessage) SourcePositions(com.sun.source.util.SourcePositions) IntersectionTypeTree(com.sun.source.tree.IntersectionTypeTree) FunctionalSwitchExpressionScanner(org.checkerframework.javacutil.SwitchExpressionScanner.FunctionalSwitchExpressionScanner) ElementUtils(org.checkerframework.javacutil.ElementUtils) ContractsFromMethod(org.checkerframework.framework.util.ContractsFromMethod) CollectionsPlume(org.plumelib.util.CollectionsPlume) PurityUtils(org.checkerframework.dataflow.util.PurityUtils) JointVisitorWithDefaultAction(org.checkerframework.framework.ajava.JointVisitorWithDefaultAction) AnnotatedIntersectionType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedIntersectionType) BugInCF(org.checkerframework.javacutil.BugInCF) PurityChecker(org.checkerframework.dataflow.util.PurityChecker) JCFieldAccess(com.sun.tools.javac.tree.JCTree.JCFieldAccess) AnnotatedTypeTree(com.sun.source.tree.AnnotatedTypeTree) InsertAjavaAnnotations(org.checkerframework.framework.ajava.InsertAjavaAnnotations) JCMemberReference(com.sun.tools.javac.tree.JCTree.JCMemberReference) IdentifierTree(com.sun.source.tree.IdentifierTree) CatchTree(com.sun.source.tree.CatchTree) NewArrayTree(com.sun.source.tree.NewArrayTree) CompilationUnit(com.github.javaparser.ast.CompilationUnit) Pair(org.checkerframework.javacutil.Pair) ArraysPlume(org.plumelib.util.ArraysPlume) ReferenceMode(com.sun.source.tree.MemberReferenceTree.ReferenceMode) Analysis(org.checkerframework.dataflow.analysis.Analysis) WholeProgramInference(org.checkerframework.common.wholeprograminference.WholeProgramInference) ExpectedTreesVisitor(org.checkerframework.framework.ajava.ExpectedTreesVisitor) CFAbstractStore(org.checkerframework.framework.flow.CFAbstractStore) Contract(org.checkerframework.framework.util.Contract) CompilationUnitTree(com.sun.source.tree.CompilationUnitTree) AnnotatedTypeParameterBounds(org.checkerframework.framework.type.AnnotatedTypeParameterBounds) TypeKind(javax.lang.model.type.TypeKind) List(java.util.List) LocalVariable(org.checkerframework.dataflow.expression.LocalVariable) AnnotatedDeclaredType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType) TypeHierarchy(org.checkerframework.framework.type.TypeHierarchy) Annotation(java.lang.annotation.Annotation) ModifiersTree(com.sun.source.tree.ModifiersTree) AnnotatedTypes(org.checkerframework.framework.util.AnnotatedTypes) Postcondition(org.checkerframework.framework.util.Contract.Postcondition) AnnotatedTypeVariable(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable) AnnotationTree(com.sun.source.tree.AnnotationTree) MethodTree(com.sun.source.tree.MethodTree) HashMap(java.util.HashMap) HashSet(java.util.HashSet) Kind(javax.tools.Diagnostic.Kind) AnnotationBuilder(org.checkerframework.javacutil.AnnotationBuilder) AnnotationUtils(org.checkerframework.javacutil.AnnotationUtils) ClassTree(com.sun.source.tree.ClassTree) Nullable(org.checkerframework.checker.nullness.qual.Nullable) Name(javax.lang.model.element.Name) JavaExpressionParseException(org.checkerframework.framework.util.JavaExpressionParseUtil.JavaExpressionParseException) ElementKind(javax.lang.model.element.ElementKind) MemberReferenceTree(com.sun.source.tree.MemberReferenceTree) ExecutableElement(javax.lang.model.element.ExecutableElement) ReturnNode(org.checkerframework.dataflow.cfg.node.ReturnNode) JavaExpression(org.checkerframework.dataflow.expression.JavaExpression) JCTree(com.sun.tools.javac.tree.JCTree) ElementType(java.lang.annotation.ElementType) SimpleAnnotatedTypeScanner(org.checkerframework.framework.type.visitor.SimpleAnnotatedTypeScanner) JavaExpressionScanner(org.checkerframework.dataflow.expression.JavaExpressionScanner) AnnotatedTypeFactory(org.checkerframework.framework.type.AnnotatedTypeFactory) AnnotatedUnionType(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedUnionType) TypeMirror(javax.lang.model.type.TypeMirror) StringJoiner(java.util.StringJoiner) ProcessingEnvironment(javax.annotation.processing.ProcessingEnvironment) Deterministic(org.checkerframework.dataflow.qual.Deterministic) JavaParserUtil(org.checkerframework.framework.util.JavaParserUtil) Collections(java.util.Collections) Node(org.checkerframework.dataflow.cfg.node.Node) DefaultQualifier(org.checkerframework.framework.qual.DefaultQualifier) InputStream(java.io.InputStream) AnnotationMirror(javax.lang.model.element.AnnotationMirror) StringToJavaExpression(org.checkerframework.framework.util.StringToJavaExpression) JavaExpression(org.checkerframework.dataflow.expression.JavaExpression) DiagMessage(org.checkerframework.framework.source.DiagMessage) StringToJavaExpression(org.checkerframework.framework.util.StringToJavaExpression) JavaExpressionParseException(org.checkerframework.framework.util.JavaExpressionParseUtil.JavaExpressionParseException) BugInCF(org.checkerframework.javacutil.BugInCF) Contract(org.checkerframework.framework.util.Contract)

Aggregations

ConditionalPostcondition (org.checkerframework.framework.util.Contract.ConditionalPostcondition)3 ContractsFromMethod (org.checkerframework.framework.util.ContractsFromMethod)3 ClassTree (com.sun.source.tree.ClassTree)2 ExpressionTree (com.sun.source.tree.ExpressionTree)2 MethodTree (com.sun.source.tree.MethodTree)2 Tree (com.sun.source.tree.Tree)2 TreePath (com.sun.source.util.TreePath)2 ClassSymbol (com.sun.tools.javac.code.Symbol.ClassSymbol)2 ArrayList (java.util.ArrayList)2 Arrays (java.util.Arrays)2 Collections (java.util.Collections)2 HashMap (java.util.HashMap)2 HashSet (java.util.HashSet)2 List (java.util.List)2 Map (java.util.Map)2 Set (java.util.Set)2 AnnotationMirror (javax.lang.model.element.AnnotationMirror)2 Element (javax.lang.model.element.Element)2 ElementKind (javax.lang.model.element.ElementKind)2 ExecutableElement (javax.lang.model.element.ExecutableElement)2