Skip to content

Commit

Permalink
JEP-445: support unnamed classes
Browse files Browse the repository at this point in the history
Adds a compiler AST UnnamedClass type and grammar rules to parse unnamed
classes.
Sets up validation and (in)visibility rules.
Codegen.
With some tests.
  • Loading branch information
mickaelistria committed Oct 20, 2023
1 parent 398ba0b commit 0b2891f
Show file tree
Hide file tree
Showing 37 changed files with 2,229 additions and 1,777 deletions.
29 changes: 22 additions & 7 deletions org.eclipse.jdt.core.compiler.batch/grammar/java.g
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
--main options
%options ACTION, AN=JavaAction.java, GP=java,
%options FILE-PREFIX=java, ESCAPE=$, PREFIX=TokenName, OUTPUT-SIZE=125 ,
%options NOGOTO-DEFAULT, SINGLE-PRODUCTIONS, LALR=1 , TABLE,
%options ACTION
%options AN=JavaAction.java
%options GP=java,
%options FILE-PREFIX=java
%options ESCAPE=$
%options PREFIX=TokenName
%options OUTPUT-SIZE=125
%options NOGOTO-DEFAULT
%options SINGLE-PRODUCTIONS
%options LALR=1
%options TABLE

--error recovering options.....
%options ERROR_MAPS
Expand Down Expand Up @@ -432,10 +440,6 @@ InternalCompilationUnit ::= PackageDeclaration TypeDeclarations
/.$putCase consumeInternalCompilationUnitWithTypes(); $break ./
InternalCompilationUnit ::= ImportDeclarations ReduceImports
/.$putCase consumeInternalCompilationUnit(); $break ./
InternalCompilationUnit ::= TypeDeclarations
/.$putCase consumeInternalCompilationUnitWithTypes(); $break ./
InternalCompilationUnit ::= ImportDeclarations ReduceImports TypeDeclarations
/.$putCase consumeInternalCompilationUnitWithTypes(); $break ./
InternalCompilationUnit ::= $empty
/.$putCase consumeEmptyInternalCompilationUnit(); $break ./
/:$readableName CompilationUnit:/
Expand All @@ -451,6 +455,12 @@ ModuleDeclaration ::= ModuleHeader ModuleBody
/:$compliance 9:/
/.$putCase consumeModuleDeclaration(); $break ./

-- JEP 445: unnamed class, this may capture type declarations without unnamed class, this case is fixed/reduced upon completioon of parsing
InternalCompilationUnit ::= UnnamedClassBodyDeclarations
/.$putCase consumeInternalCompilationUnitWithPotentialUnnamedClass(); $break ./
InternalCompilationUnit ::= ImportDeclarations ReduceImports UnnamedClassBodyDeclarations
/.$putCase consumeInternalCompilationUnitWithPotentialUnnamedClass(); $break ./

-- to work around shift/reduce conflicts, we allow Modifiersopt in order to support annotations
-- in a module declaration, and then report errors if any modifiers other than annotations are
-- encountered
Expand Down Expand Up @@ -744,6 +754,11 @@ ClassBodyDeclarations ::= ClassBodyDeclarations ClassBodyDeclaration
ClassBodyDeclaration -> ClassMemberDeclaration
ClassBodyDeclaration -> StaticInitializer
ClassBodyDeclaration -> ConstructorDeclaration

UnnamedClassBodyDeclarations -> ClassMemberDeclaration
UnnamedClassBodyDeclarations -> ClassMemberDeclaration UnnamedClassBodyDeclarations
/.$putCase consumeUnnamedClassBodyDeclarations(); $break ./
/:$readableName UnnamedClassBodyDeclarations:/
--1.1 feature
ClassBodyDeclaration ::= Diet NestedMethod CreateInitializer Block
/.$putCase consumeClassBodyDeclaration(); $break ./
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2553,6 +2553,17 @@ public interface IProblem {
* @noreference preview feature
*/
int CannotInferRecordPatternTypes = PreviewRelated + 1940;

/**
* @since 3.36
* @noreference preview feature
*/
int unnamedIsMissingMainMethod = PreviewRelated + 1942;
/**
* @since 3.36
* @noreference preview feature
*/
int unnamedRequiresJava21 = PreviewRelated + 1943;

/**
* @since 3.36
Expand All @@ -2564,4 +2575,5 @@ public interface IProblem {
* @since 3.35
*/
int SyntheticAccessorNotEnclosingMethod = MethodRelated + 1990;

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import java.util.Arrays;
import java.util.List;
import java.util.function.BiPredicate;

Expand Down Expand Up @@ -443,4 +444,13 @@ public void traverse(
public TypeParameter[] typeParameters() {
return this.typeParameters;
}

public boolean isMainMethodCandidate() {
return "main".equals(new String(this.selector)) && //$NON-NLS-1$
(this.arguments == null || this.arguments.length == 0 ||
(this.arguments.length == 1 &&
this.arguments[0].type.getTypeBinding(this.scope) instanceof ReferenceBinding typeRef &&
typeRef.dimensions() == 1 &&
Arrays.equals("java.lang.String".toCharArray(), CharOperation.concatWith(typeRef.compoundName, '.')))); //$NON-NLS-1$
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*******************************************************************************
* Copyright (c) 2023 Red Hat, Inc. 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
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import java.util.Arrays;

import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;

/**
* Represents an unnamed class as defined in JEP 445
*/
public class UnnamedClass extends TypeDeclaration {

public UnnamedClass(CompilationResult result) {
super(result);
this.modifiers = ClassFileConstants.AccDefault | ClassFileConstants.AccFinal;
this.name = "<unnamed class>".toCharArray(); //$NON-NLS-1$
}

@Override
public void analyseCode(CompilationUnitScope unitScope) {
super.analyseCode(unitScope);
if (this.ignoreFurtherInvestigation)
return;
if (Arrays.stream(this.methods)
.filter(MethodDeclaration.class::isInstance)
.map(MethodDeclaration.class::cast)
.noneMatch(MethodDeclaration::isMainMethodCandidate)) {
unitScope.problemReporter().unnamedClassMustHaveMainMethod();
this.ignoreFurtherInvestigation = true;
}
}

// @Override
// public void generateCode(CompilationUnitScope unitScope) {
// SourceTypeBinding unnamedBinding = this.binding;
// this.binding = toNamedBinding(unitScope);
// super.generateCode(unitScope);
// this.binding = unnamedBinding;
// }
//
// private SourceTypeBinding toNamedBinding(CompilationUnitScope scope) {
// SourceTypeBinding res = new SourceTypeBinding(this.binding) {
// @Override
// public boolean isPrototype() {
// return true;
// }
// };
// res.compoundName = new char[][] { scope.referenceContext.getMainTypeName() };
// return res;
// }

}
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,9 @@ public ReferenceBinding findType(
if (declarationPackage != invocationPackage && !typeBinding.canBeSeenBy(invocationPackage))
return new ProblemReferenceBinding(new char[][]{typeName}, typeBinding, ProblemReasons.NotVisible);
}
if (typeBinding instanceof SourceTypeBinding sourceType && sourceType.scope.referenceContext instanceof UnnamedClass) {
return null; // make unnamed not resolvable by name
}
return typeBinding;
}

Expand Down Expand Up @@ -3529,9 +3532,10 @@ final Binding getTypeOrPackage(char[] name, int mask, boolean needResolve) {
PackageBinding currentPackage = unitScope.fPackage;
unitScope.recordReference(currentPackage.compoundName, name);
Binding binding = currentPackage.getTypeOrPackage(name, module(), false);
if (binding instanceof ReferenceBinding) {
ReferenceBinding referenceType = (ReferenceBinding) binding;
if ((referenceType.tagBits & TagBits.HasMissingType) == 0) {
if (binding instanceof ReferenceBinding referenceType) {
if (referenceType instanceof SourceTypeBinding sourceType && sourceType.unnamedClass) {
return new ProblemReferenceBinding(new char[][] {name}, null, ProblemReasons.NotAccessible);
} else if ((referenceType.tagBits & TagBits.HasMissingType) == 0) {
if (typeOrPackageCache != null)
typeOrPackageCache.put(name, referenceType);
return referenceType; // type is always visible to its own package
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.UnnamedClass;
import org.eclipse.jdt.internal.compiler.ast.TypeReference.AnnotationPosition;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider;
Expand Down Expand Up @@ -138,9 +139,13 @@ public class SourceTypeBinding extends ReferenceBinding {
public boolean isVarArgs = false; // for record declaration
private FieldBinding[] implicitComponentFields; // cache
private MethodBinding[] recordComponentAccessors = null; // hash maybe an overkill
public final boolean unnamedClass;

public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage, ClassScope scope) {
this.compoundName = compoundName;
this.unnamedClass = scope.referenceContext instanceof UnnamedClass;
this.compoundName = scope.referenceContext instanceof UnnamedClass unnamed
? new char[][] { unnamed.getCompilationUnitDeclaration().getMainTypeName() }
: compoundName;
this.fPackage = fPackage;
this.fileName = scope.referenceCompilationUnit().getFileName();
this.modifiers = scope.referenceContext.modifiers;
Expand All @@ -163,6 +168,7 @@ public SourceTypeBinding(SourceTypeBinding prototype) {
this.prototype = prototype.prototype;
this.prototype.tagBits |= TagBits.HasAnnotatedVariants;
this.tagBits &= ~TagBits.HasAnnotatedVariants;
this.unnamedClass = prototype.unnamedClass;

this.superclass = prototype.superclass;
this.superInterfaces = prototype.superInterfaces;
Expand Down
Loading

0 comments on commit 0b2891f

Please sign in to comment.