<commit_before>package kalang.compiler;

import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import kalang.AstNotFoundException;
import kalang.antlr.KalangParser;
import kalang.antlr.KalangParser.MethodDeclContext;
import kalang.antlr.KalangParserBaseVisitor;
import kalang.ast.AnnotationNode;
import kalang.ast.AssignExpr;
import kalang.ast.AssignableExpr;
import kalang.ast.BlockStmt;
import kalang.ast.ClassNode;
import kalang.ast.ClassReference;
import kalang.ast.ExprNode;
import kalang.ast.ExprStmt;
import kalang.ast.FieldNode;
import kalang.ast.MethodNode;
import kalang.ast.ObjectFieldExpr;
import kalang.ast.ParameterExpr;
import kalang.ast.ParameterNode;
import kalang.ast.StaticFieldExpr;
import kalang.ast.ThisExpr;
import kalang.core.GenericType;
import kalang.core.MethodDescriptor;
import kalang.core.ModifierConstant;
import kalang.core.NullableKind;
import kalang.core.ObjectType;
import kalang.core.Type;
import kalang.core.Types;
import kalang.exception.Exceptions;
import kalang.util.AstUtil;
import kalang.util.ClassTypeUtil;
import kalang.util.MethodUtil;
import kalang.util.ModifierUtil;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;

/**
 *
 * @author <NAME>
 */
public class ClassNodeMetaBuilder extends KalangParserBaseVisitor<Object> {

    AstBuilder astBuilder;
    private final ClassNodeBuilder classNodeBuilder;
    private ClassNode thisClazz;
    
    private Map<MethodNode,KalangParser.StatContext[]> methodStatsContexts = new HashMap();
    
    private Map<MethodNode,MethodDeclContext> methodContexts = new HashMap();
    
    private MethodNode method;

    private final CompilationUnit compilationUnit;
    private DiagnosisReporter diagnosisReporter;
    
    public ClassNodeMetaBuilder(CompilationUnit compilationUnit, AstBuilder astBuilder, ClassNodeBuilder classNodeBuilder) {
        this.compilationUnit = compilationUnit;
        this.astBuilder = astBuilder;
        this.classNodeBuilder = classNodeBuilder;
        this.diagnosisReporter = new DiagnosisReporter(this.compilationUnit);
    }

    public void build(ClassNode cn,boolean inScriptMode) {
        this.thisClazz = cn;
        astBuilder.thisClazz = cn;
        ParserRuleContext ctx = classNodeBuilder.getClassNodeDefContext(cn);
        if(ctx!=null) visit(ctx);
        for (ClassNode c : cn.classes) {
            build(c,false);
        }
    }    

    @Override
    public Object visitClassDef(KalangParser.ClassDefContext ctx) {
        thisClazz.annotations.addAll(astBuilder.getAnnotations(ctx.annotation()));
        thisClazz.modifier = astBuilder.parseModifier(ctx.varModifier());
        List<Token> gnrTypes = ctx.genericTypes;
        if (gnrTypes != null && !gnrTypes.isEmpty()) {
            for (Token g : gnrTypes) {
                //TODO suport generic type bounds in syntax
                GenericType gt = new GenericType(g.getText(), Types.getRootType(), null, NullableKind.NONNULL);
                thisClazz.declareGenericType(gt);
            }
        }
        ObjectType superType = null;
        if (ctx.parentClass != null) {
            ObjectType parentClass = astBuilder.parseClassType(ctx.parentClass);
            if (parentClass != null) {
                superType = parentClass;
            }
        } else {
            superType = Types.getRootType();
        }
        if (Modifier.isInterface(thisClazz.modifier)) {
            //TODO update syntax to support:interface extends T1,T2...
            thisClazz.addInterface(superType);
        } else {
            thisClazz.setSuperType(superType);
        }
        if (ctx.interfaces != null && ctx.interfaces.size() > 0) {
            for (KalangParser.ClassTypeContext itf : ctx.interfaces) {
                ObjectType itfClz = astBuilder.parseClassType(itf);
                if (itfClz != null) {
                    thisClazz.addInterface(itfClz);
                }
            }
        }
        if (this.isDeclaringNonStaticInnerClass()) {
            ClassNode parentClass = thisClazz.enclosingClass;
            if (parentClass == null) {
                throw Exceptions.unexceptedValue(parentClass);
            }
            thisClazz.createField(Types.getClassType(parentClass), "this$0", Modifier.PRIVATE | ModifierConstant.SYNTHETIC);
        }
        visit(ctx.classBody());
        if (!ModifierUtil.isInterface(thisClazz.modifier) 
                && !AstUtil.containsConstructor(thisClazz) 
                && !AstUtil.createEmptyConstructor(thisClazz)) {
            this.diagnosisReporter.report(Diagnosis.Kind.ERROR
                    , "failed to create constructor with no parameters", ctx
            );
        }
        MethodNode[] methods = thisClazz.getDeclaredMethodNodes();
        for (int i = 0; i < methods.length; i++) {
            MethodNode node = methods[i];
            BlockStmt body = node.getBody();
            if (body != null) {
                if (AstUtil.isConstructor(node)) {//constructor
                    if (this.isDeclaringNonStaticInnerClass()) {
                        ClassNode enclosingClass = thisClazz.enclosingClass;
                        if (enclosingClass == null) {
                            throw Exceptions.unexceptedValue(enclosingClass);
                        }
                        ParameterNode outerInstanceParam = node.createParameter(0, Types.getClassType(enclosingClass), "this$0");
                        ExprNode parentFieldExpr = astBuilder.getObjectFieldExpr(
                                new ThisExpr(Types.getClassType(thisClazz)), "this$0", ParserRuleContext.EMPTY
                        );
                        if (parentFieldExpr == null) {
                            throw Exceptions.unexceptedValue(parentFieldExpr);
                        }
                        body.statements.add(1, new ExprStmt(new AssignExpr((AssignableExpr) parentFieldExpr, new ParameterExpr(outerInstanceParam))));
                    }
                }
            }
        }
        for (FieldNode fieldNode : thisClazz.getFields()) {
            int mdf = fieldNode.modifier;
            if (!AstUtil.hasGetter(thisClazz, fieldNode)) {
                AstUtil.createGetter(thisClazz, fieldNode, mdf);
            }
            if (!AstUtil.hasSetter(thisClazz, fieldNode)) {
                AstUtil.createSetter(thisClazz, fieldNode, mdf);
            }
            fieldNode.modifier = ModifierUtil.setPrivate(mdf);
        }<commit_msg>fix: fix getter and setter generation<commit_after>package kalang.compiler;

import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import kalang.AstNotFoundException;
import kalang.antlr.KalangParser;
import kalang.antlr.KalangParser.MethodDeclContext;
import kalang.antlr.KalangParserBaseVisitor;
import kalang.ast.AnnotationNode;
import kalang.ast.AssignExpr;
import kalang.ast.AssignableExpr;
import kalang.ast.BlockStmt;
import kalang.ast.ClassNode;
import kalang.ast.ClassReference;
import kalang.ast.ExprNode;
import kalang.ast.ExprStmt;
import kalang.ast.FieldNode;
import kalang.ast.MethodNode;
import kalang.ast.ObjectFieldExpr;
import kalang.ast.ParameterExpr;
import kalang.ast.ParameterNode;
import kalang.ast.StaticFieldExpr;
import kalang.ast.ThisExpr;
import kalang.core.GenericType;
import kalang.core.MethodDescriptor;
import kalang.core.ModifierConstant;
import kalang.core.NullableKind;
import kalang.core.ObjectType;
import kalang.core.Type;
import kalang.core.Types;
import kalang.exception.Exceptions;
import kalang.util.AstUtil;
import kalang.util.ClassTypeUtil;
import kalang.util.MethodUtil;
import kalang.util.ModifierUtil;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;

/**
 *
 * @author <NAME>
 */
public class ClassNodeMetaBuilder extends KalangParserBaseVisitor<Object> {

    AstBuilder astBuilder;
    private final ClassNodeBuilder classNodeBuilder;
    private ClassNode thisClazz;
    
    private Map<MethodNode,KalangParser.StatContext[]> methodStatsContexts = new HashMap();
    
    private Map<MethodNode,MethodDeclContext> methodContexts = new HashMap();
    
    private MethodNode method;

    private final CompilationUnit compilationUnit;
    private DiagnosisReporter diagnosisReporter;
    
    public ClassNodeMetaBuilder(CompilationUnit compilationUnit, AstBuilder astBuilder, ClassNodeBuilder classNodeBuilder) {
        this.compilationUnit = compilationUnit;
        this.astBuilder = astBuilder;
        this.classNodeBuilder = classNodeBuilder;
        this.diagnosisReporter = new DiagnosisReporter(this.compilationUnit);
    }

    public void build(ClassNode cn,boolean inScriptMode) {
        this.thisClazz = cn;
        astBuilder.thisClazz = cn;
        ParserRuleContext ctx = classNodeBuilder.getClassNodeDefContext(cn);
        if(ctx!=null) visit(ctx);
        for (ClassNode c : cn.classes) {
            build(c,false);
        }
    }    

    @Override
    public Object visitClassDef(KalangParser.ClassDefContext ctx) {
        thisClazz.annotations.addAll(astBuilder.getAnnotations(ctx.annotation()));
        thisClazz.modifier = astBuilder.parseModifier(ctx.varModifier());
        List<Token> gnrTypes = ctx.genericTypes;
        if (gnrTypes != null && !gnrTypes.isEmpty()) {
            for (Token g : gnrTypes) {
                //TODO suport generic type bounds in syntax
                GenericType gt = new GenericType(g.getText(), Types.getRootType(), null, NullableKind.NONNULL);
                thisClazz.declareGenericType(gt);
            }
        }
        ObjectType superType = null;
        if (ctx.parentClass != null) {
            ObjectType parentClass = astBuilder.parseClassType(ctx.parentClass);
            if (parentClass != null) {
                superType = parentClass;
            }
        } else {
            superType = Types.getRootType();
        }
        if (Modifier.isInterface(thisClazz.modifier)) {
            //TODO update syntax to support:interface extends T1,T2...
            thisClazz.addInterface(superType);
        } else {
            thisClazz.setSuperType(superType);
        }
        if (ctx.interfaces != null && ctx.interfaces.size() > 0) {
            for (KalangParser.ClassTypeContext itf : ctx.interfaces) {
                ObjectType itfClz = astBuilder.parseClassType(itf);
                if (itfClz != null) {
                    thisClazz.addInterface(itfClz);
                }
            }
        }
        if (this.isDeclaringNonStaticInnerClass()) {
            ClassNode parentClass = thisClazz.enclosingClass;
            if (parentClass == null) {
                throw Exceptions.unexceptedValue(parentClass);
            }
            thisClazz.createField(Types.getClassType(parentClass), "this$0", Modifier.PRIVATE | ModifierConstant.SYNTHETIC);
        }
        visit(ctx.classBody());
        if (!ModifierUtil.isInterface(thisClazz.modifier) 
                && !AstUtil.containsConstructor(thisClazz) 
                && !AstUtil.createEmptyConstructor(thisClazz)) {
            this.diagnosisReporter.report(Diagnosis.Kind.ERROR
                    , "failed to create constructor with no parameters", ctx
            );
        }
        MethodNode[] methods = thisClazz.getDeclaredMethodNodes();
        for (int i = 0; i < methods.length; i++) {
            MethodNode node = methods[i];
            BlockStmt body = node.getBody();
            if (body != null) {
                if (AstUtil.isConstructor(node)) {//constructor
                    if (this.isDeclaringNonStaticInnerClass()) {
                        ClassNode enclosingClass = thisClazz.enclosingClass;
                        if (enclosingClass == null) {
                            throw Exceptions.unexceptedValue(enclosingClass);
                        }
                        ParameterNode outerInstanceParam = node.createParameter(0, Types.getClassType(enclosingClass), "this$0");
                        ExprNode parentFieldExpr = astBuilder.getObjectFieldExpr(
                                new ThisExpr(Types.getClassType(thisClazz)), "this$0", ParserRuleContext.EMPTY
                        );
                        if (parentFieldExpr == null) {
                            throw Exceptions.unexceptedValue(parentFieldExpr);
                        }
                        body.statements.add(1, new ExprStmt(new AssignExpr((AssignableExpr) parentFieldExpr, new ParameterExpr(outerInstanceParam))));
                    }
                }
            }
        }
        for (FieldNode fieldNode : thisClazz.getFields()) {
            int mdf = fieldNode.modifier;
            if (Modifier.isStatic(mdf)){
                continue;
            }
            if (!Modifier.isPublic(mdf) && !Modifier.isProtected(mdf)){
                continue;
            }
            if (!AstUtil.hasGetter(thisClazz, fieldNode)) {
                AstUtil.createGetter(thisClazz, fieldNode, mdf);
            }
            if (!AstUtil.hasSetter(thisClazz, fieldNode)) {
                AstUtil.createSetter(thisClazz, fieldNode, mdf);
            }
            fieldNode.modifier = ModifierUtil.setPrivate(mdf);
        }
{'fraction_non_alphanumeric': 0.09296426173244796, 'fraction_numerical': 0.0016414235618891293, 'mean_word_length': 2.4046228092456183, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5932819', 'n_tokens_mistral': 3784, 'n_tokens_neox': 3687, 'n_words': 682}