org.eclipse.jdt.internal.compiler.ast.AssertStatement.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.ast.AssertStatement.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2013 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Stephan Herrmann - Contributions for
 *                        bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE
 *                        bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
 *                        bug 403086 - [compiler][null] include the effect of 'assert' in syntactic null analysis for fields
 *                        bug 403147 - [compiler][null] FUP of bug 400761: consolidate interaction between unboxing, NPE, and deferred checking
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.ASTVisitor;

public class AssertStatement extends Statement {

    public Expression assertExpression, exceptionArgument;

    // for local variable attribute
    int preAssertInitStateIndex = -1;
    private FieldBinding assertionSyntheticFieldBinding;

    public AssertStatement(Expression exceptionArgument, Expression assertExpression, int startPosition) {
        this.assertExpression = assertExpression;
        this.exceptionArgument = exceptionArgument;
        this.sourceStart = startPosition;
        this.sourceEnd = exceptionArgument.sourceEnd;
    }

    public AssertStatement(Expression assertExpression, int startPosition) {
        this.assertExpression = assertExpression;
        this.sourceStart = startPosition;
        this.sourceEnd = assertExpression.sourceEnd;
    }

    @Override
    public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
        this.preAssertInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo);

        Constant cst = this.assertExpression.optimizedBooleanConstant();
        this.assertExpression.checkNPEbyUnboxing(currentScope, flowContext, flowInfo);
        boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true;
        boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false;

        flowContext.tagBits |= FlowContext.HIDE_NULL_COMPARISON_WARNING;
        FlowInfo conditionFlowInfo = this.assertExpression.analyseCode(currentScope, flowContext, flowInfo.copy());
        flowContext.extendTimeToLiveForNullCheckedField(1); // survive this assert as a Statement
        flowContext.tagBits &= ~FlowContext.HIDE_NULL_COMPARISON_WARNING;
        UnconditionalFlowInfo assertWhenTrueInfo = conditionFlowInfo.initsWhenTrue().unconditionalInits();
        FlowInfo assertInfo = conditionFlowInfo.initsWhenFalse();
        if (isOptimizedTrueAssertion) {
            assertInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
        }

        if (this.exceptionArgument != null) {
            // only gets evaluated when escaping - results are not taken into account
            FlowInfo exceptionInfo = this.exceptionArgument.analyseCode(currentScope, flowContext,
                    assertInfo.copy());

            if (isOptimizedTrueAssertion) {
                currentScope.problemReporter().fakeReachable(this.exceptionArgument);
            } else {
                flowContext.checkExceptionHandlers(currentScope.getJavaLangAssertionError(), this, exceptionInfo,
                        currentScope);
            }
        }

        if (!isOptimizedTrueAssertion) {
            // add the assert support in the clinit
            manageSyntheticAccessIfNecessary(currentScope, flowInfo);
        }
        // account for potential AssertionError:
        flowContext.recordAbruptExit();
        if (isOptimizedFalseAssertion) {
            return flowInfo; // if assertions are enabled, the following code will be unreachable
            // change this if we need to carry null analysis results of the assert
            // expression downstream
        } else {
            CompilerOptions compilerOptions = currentScope.compilerOptions();
            if (!compilerOptions.includeNullInfoFromAsserts) {
                // keep just the initializations info, don't include assert's null info
                // merge initialization info's and then add back the null info from flowInfo to
                // make sure that the empty null info of assertInfo doesnt change flowInfo's null info.
                return ((flowInfo.nullInfoLessUnconditionalCopy())
                        .mergedWith(assertInfo.nullInfoLessUnconditionalCopy())).addNullInfoFrom(flowInfo);
            }
            return flowInfo.mergedWith(assertInfo.nullInfoLessUnconditionalCopy())
                    .addInitializationsFrom(assertWhenTrueInfo.discardInitializationInfo());
            // keep the merge from the initial code for the definite assignment
            // analysis, tweak the null part to influence nulls downstream
        }
    }

    @Override
    public void generateCode(BlockScope currentScope, CodeStream codeStream) {
        if ((this.bits & IsReachable) == 0) {
            return;
        }
        int pc = codeStream.position;

        if (this.assertionSyntheticFieldBinding != null) {
            BranchLabel assertionActivationLabel = new BranchLabel(codeStream);
            codeStream.fieldAccess(Opcodes.OPC_getstatic, this.assertionSyntheticFieldBinding,
                    null /* default declaringClass */);
            codeStream.ifne(assertionActivationLabel);

            BranchLabel falseLabel;
            this.assertExpression.generateOptimizedBoolean(currentScope, codeStream,
                    (falseLabel = new BranchLabel(codeStream)), null, true);
            codeStream.newJavaLangAssertionError();
            codeStream.dup();
            if (this.exceptionArgument != null) {
                this.exceptionArgument.generateCode(currentScope, codeStream, true);
                codeStream.invokeJavaLangAssertionErrorConstructor(this.exceptionArgument.implicitConversion & 0xF);
            } else {
                codeStream.invokeJavaLangAssertionErrorDefaultConstructor();
            }
            codeStream.athrow();

            // May loose some local variable initializations : affecting the local variable attributes
            if (this.preAssertInitStateIndex != -1) {
                codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preAssertInitStateIndex);
            }
            falseLabel.place();
            assertionActivationLabel.place();
        } else {
            // May loose some local variable initializations : affecting the local variable attributes
            if (this.preAssertInitStateIndex != -1) {
                codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preAssertInitStateIndex);
            }
        }
        codeStream.recordPositionsFrom(pc, this.sourceStart);
    }

    @Override
    public void resolve(BlockScope scope) {
        this.assertExpression.resolveTypeExpecting(scope, TypeBinding.BOOLEAN);
        if (this.exceptionArgument != null) {
            TypeBinding exceptionArgumentType = this.exceptionArgument.resolveType(scope);
            if (exceptionArgumentType != null) {
                int id = exceptionArgumentType.id;
                switch (id) {
                case T_void:
                    scope.problemReporter().illegalVoidExpression(this.exceptionArgument);
                    //$FALL-THROUGH$
                default:
                    id = T_JavaLangObject;
                    //$FALL-THROUGH$
                case T_boolean:
                case T_byte:
                case T_char:
                case T_short:
                case T_double:
                case T_float:
                case T_int:
                case T_long:
                case T_JavaLangString:
                    this.exceptionArgument.implicitConversion = (id << 4) + id;
                }
            }
        }
    }

    @Override
    public void traverse(ASTVisitor visitor, BlockScope scope) {
        if (visitor.visit(this, scope)) {
            this.assertExpression.traverse(visitor, scope);
            if (this.exceptionArgument != null) {
                this.exceptionArgument.traverse(visitor, scope);
            }
        }
        visitor.endVisit(this, scope);
    }

    public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
        if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) {
            // need assertion flag: $assertionsDisabled on outer most source clas
            // (in case of static member of interface, will use the outermost static member - bug 22334)
            SourceTypeBinding outerMostClass = currentScope.enclosingSourceType();
            while (outerMostClass.isLocalType()) {
                ReferenceBinding enclosing = outerMostClass.enclosingType();
                if (enclosing == null || enclosing.isInterface())
                    break;
                outerMostClass = (SourceTypeBinding) enclosing;
            }
            this.assertionSyntheticFieldBinding = outerMostClass.addSyntheticFieldForAssert(currentScope);

            // find <clinit> and enable assertion support
            TypeDeclaration typeDeclaration = outerMostClass.scope.referenceType();
            AbstractMethodDeclaration[] methods = typeDeclaration.methods;
            for (int i = 0, max = methods.length; i < max; i++) {
                AbstractMethodDeclaration method = methods[i];
                if (method.isClinit()) {
                    ((Clinit) method).setAssertionSupport(this.assertionSyntheticFieldBinding,
                            currentScope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_5);
                    break;
                }
            }
        }
    }

    @Override
    public StringBuffer printStatement(int tab, StringBuffer output) {
        printIndent(tab, output);
        output.append("assert "); //$NON-NLS-1$
        this.assertExpression.printExpression(0, output);
        if (this.exceptionArgument != null) {
            output.append(": "); //$NON-NLS-1$
            this.exceptionArgument.printExpression(0, output);
        }
        return output.append(';');
    }
}