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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (c) 2000, 2017 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
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import org.eclipse.jdt.internal.compiler.ASTVisitor;
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.Constant;
import org.eclipse.jdt.internal.compiler.lookup.*;

public class ClassLiteralAccess extends Expression {

    public TypeReference type;
    public TypeBinding targetType;
    FieldBinding syntheticField;

    public ClassLiteralAccess(int sourceEnd, TypeReference type) {
        this.type = type;
        type.bits |= IgnoreRawTypeCheck; // no need to worry about raw type usage
        this.sourceStart = type.sourceStart;
        this.sourceEnd = sourceEnd;
    }

    @Override
    public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {

        // if reachable, request the addition of a synthetic field for caching the class descriptor
        SourceTypeBinding sourceType = currentScope.outerMostClassScope().enclosingSourceType();
        // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=22334
        if (!sourceType.isInterface() && !this.targetType.isBaseType()
                && currentScope.compilerOptions().targetJDK < ClassFileConstants.JDK1_5) {
            this.syntheticField = sourceType.addSyntheticFieldForClassLiteral(this.targetType, currentScope);
        }
        return flowInfo;
    }

    /**
     * MessageSendDotClass code generation
     *
     * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
     * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
     * @param valueRequired boolean
     */
    @Override
    public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
        int pc = codeStream.position;

        // in interface case, no caching occurs, since cannot make a cache field for interface
        if (valueRequired) {
            codeStream.generateClassLiteralAccessForType(currentScope, this.type.resolvedType, this.syntheticField);
            codeStream.generateImplicitConversion(this.implicitConversion);
        }
        codeStream.recordPositionsFrom(pc, this.sourceStart);
    }

    @Override
    public StringBuffer printExpression(int indent, StringBuffer output) {

        return this.type.print(0, output).append(".class"); //$NON-NLS-1$
    }

    @Override
    public TypeBinding resolveType(BlockScope scope) {

        this.constant = Constant.NotAConstant;
        if ((this.targetType = this.type.resolveType(scope, true /* check bounds*/)) == null)
            return null;

        /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=320463
           https://bugs.eclipse.org/bugs/show_bug.cgi?id=312076
           JLS3 15.8.2 forbids the type named in the class literal expression from being a parameterized type.
           And the grammar in 18.1 disallows (where X and Y are some concrete types) constructs of the form
           Outer<X>.class, Outer<X>.Inner.class, Outer.Inner<X>.class, Outer<X>.Inner<Y>.class etc.
           Corollary wise, we should resolve the type of the class literal expression to be a raw type as
           class literals exist only for the raw underlying type. 
         */
        LookupEnvironment environment = scope.environment();
        this.targetType = environment.convertToRawType(this.targetType,
                true /* force conversion of enclosing types*/);

        if (this.targetType.isArrayType()) {
            ArrayBinding arrayBinding = (ArrayBinding) this.targetType;
            TypeBinding leafComponentType = arrayBinding.leafComponentType;
            if (leafComponentType == TypeBinding.VOID) {
                scope.problemReporter().cannotAllocateVoidArray(this);
                return null;
            } else if (leafComponentType.isTypeVariable()) {
                scope.problemReporter().illegalClassLiteralForTypeVariable((TypeVariableBinding) leafComponentType,
                        this);
            }
        } else if (this.targetType.isTypeVariable()) {
            scope.problemReporter().illegalClassLiteralForTypeVariable((TypeVariableBinding) this.targetType, this);
        }
        ReferenceBinding classType = scope.getJavaLangClass();
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=328689
        if (scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
            // Integer.class --> Class<Integer>, perform boxing of base types (int.class --> Class<Integer>)
            TypeBinding boxedType = null;
            if (this.targetType.id == T_void) {
                boxedType = environment.getResolvedJavaBaseType(JAVA_LANG_VOID, scope);
            } else {
                boxedType = scope.boxing(this.targetType);
            }
            if (environment.usesNullTypeAnnotations())
                boxedType = environment.createAnnotatedType(boxedType,
                        new AnnotationBinding[] { environment.getNonNullAnnotation() });
            this.resolvedType = environment.createParameterizedType(classType, new TypeBinding[] { boxedType },
                    null/*not a member*/);
        } else {
            this.resolvedType = classType;
        }
        return this.resolvedType;
    }

    @Override
    public void traverse(ASTVisitor visitor, BlockScope blockScope) {

        if (visitor.visit(this, blockScope)) {
            this.type.traverse(visitor, blockScope);
        }
        visitor.endVisit(this, blockScope);
    }
}