diff --git a/src/main/kotlin/org/casc/lang/ast/Function.kt b/src/main/kotlin/org/casc/lang/ast/Function.kt index 447a2cfe..2807aa44 100644 --- a/src/main/kotlin/org/casc/lang/ast/Function.kt +++ b/src/main/kotlin/org/casc/lang/ast/Function.kt @@ -26,7 +26,7 @@ data class Function( parameterTypes?.fold("") { s, type -> s + type?.descriptor } - })${returnType?.descriptor}" + })${returnType?.descriptor ?: "V"}" override val flag: Int by lazy { var flag = accessor.access flag += selfKeyword.getOrElse(0, Opcodes.ACC_STATIC) @@ -35,6 +35,7 @@ data class Function( flag += mutKeyword.getOrElse(0, Opcodes.ACC_FINAL) flag } + var abstract: Boolean = abstrKeyword != null override fun asSignature() = FunctionSignature( diff --git a/src/main/kotlin/org/casc/lang/checker/Checker.kt b/src/main/kotlin/org/casc/lang/checker/Checker.kt index 1c87c409..dfa28cca 100644 --- a/src/main/kotlin/org/casc/lang/checker/Checker.kt +++ b/src/main/kotlin/org/casc/lang/checker/Checker.kt @@ -98,8 +98,8 @@ class Checker(private val preference: AbstractPreference) { if (clazz.traitImpls != null) { clazz.traitImpls!!.forEach { - it.functions.forEach { - + it.functions.forEach { function -> + checkFunction(function, classScope, it.implementedTraitReference) } } } @@ -143,8 +143,8 @@ class Checker(private val preference: AbstractPreference) { if (trait.traitImpls != null) { trait.traitImpls!!.forEach { - it.functions.forEach { - + it.functions.forEach { function -> + checkFunction(function, traitScope, it.implementedTraitReference) } } } @@ -285,6 +285,23 @@ class Checker(private val preference: AbstractPreference) { } } } + + if (clazz.traitImpls != null) { + for (traitImpl in clazz.traitImpls!!) { + traitImpl.functions.forEach { + if (it.statements != null) { + checkFunctionBody(it, Scope(classScope, isCompScope = it.selfKeyword == null)) + + if (!checkControlFlow(it.statements, it.returnType)) { + // Not all code path returns value + reports += Error( + it.name?.pos, "Not all code path returns value" + ) + } + } + } + } + } } private fun checkTrait(file: File, trait: TraitInstance, traitScope: Scope) { @@ -316,6 +333,23 @@ class Checker(private val preference: AbstractPreference) { } } } + + if (trait.traitImpls != null) { + for (traitImpl in trait.traitImpls!!) { + traitImpl.functions.forEach { + if (it.statements != null) { + checkFunctionBody(it, Scope(traitScope, isCompScope = it.selfKeyword == null)) + + if (!checkControlFlow(it.statements, it.returnType)) { + // Not all code path returns value + reports += Error( + it.name?.pos, "Not all code path returns value" + ) + } + } + } + } + } } private fun checkField(field: Field, scope: Scope): Field { @@ -394,7 +428,7 @@ class Checker(private val preference: AbstractPreference) { return constructor } - private fun checkFunction(function: Function, scope: Scope, searchTraitFunction: Boolean = false): Function { + private fun checkFunction(function: Function, scope: Scope, traitReference: Reference? = null): Function { val localScope = Scope(scope) checkIdentifierIsKeyword(function.name?.literal, function.name?.pos) @@ -445,7 +479,7 @@ class Checker(private val preference: AbstractPreference) { // Check is overriding parent function val parentFunction = - scope.findSignature(scope.parentClassPath, function.name!!.literal, function.parameterTypes ?: listOf()) + scope.findSignature(traitReference ?: scope.parentClassPath, function.name!!.literal, function.parameterTypes ?: listOf()) if (parentFunction != null) { if (function.ovrdKeyword == null) { @@ -995,7 +1029,7 @@ class Checker(private val preference: AbstractPreference) { // Check function call expression's context, e.g companion context val ownerReference = expression.ownerReference ?: previousType?.getReference() ?: scope.typeReference val functionSignature = scope.findSignature( - ownerReference, expression.name!!.literal, argumentTypes + ownerReference, expression.name!!.literal, argumentTypes, allowAbstract = false ) if (functionSignature == null) { diff --git a/src/main/kotlin/org/casc/lang/compilation/Compilation.kt b/src/main/kotlin/org/casc/lang/compilation/Compilation.kt index e45f04df..58d9520d 100644 --- a/src/main/kotlin/org/casc/lang/compilation/Compilation.kt +++ b/src/main/kotlin/org/casc/lang/compilation/Compilation.kt @@ -2,10 +2,7 @@ package org.casc.lang.compilation import com.diogonunes.jcolor.AnsiFormat import com.diogonunes.jcolor.Attribute -import org.casc.lang.ast.ClassInstance -import org.casc.lang.ast.File -import org.casc.lang.ast.Token -import org.casc.lang.ast.TraitInstance +import org.casc.lang.ast.* import org.casc.lang.checker.Checker import org.casc.lang.emitter.Emitter import org.casc.lang.lexer.Lexer @@ -14,6 +11,7 @@ import org.casc.lang.table.Reference import org.casc.lang.table.Scope import org.casc.lang.table.Table import java.io.BufferedReader +import java.util.* import kotlin.time.DurationUnit import kotlin.time.ExperimentalTime @@ -117,37 +115,53 @@ class Compilation(private val preference: AbstractPreference) { return@checkPrelude } - val creationQueue = mutableListOf() + val traitQueue = mutableListOf() + val classQueue = mutableListOf() val declarations = compilationUnits.map(CompilationFileUnit::file) for ((file, compilationUnit) in declarations.zip(compilationUnits)) { // Check parent class has no cyclic inheritance val (_, _, _, typeInstance) = file - creationQueue += typeInstance.reference - when (typeInstance) { - is ClassInstance -> { - val parentClassReferenceSet = hashSetOf() - var currentTypeInstance = typeInstance - - while (currentTypeInstance.impl != null && currentTypeInstance.impl!!.parentClassReference != null) { - currentTypeInstance = - Table.findTypeInstance(currentTypeInstance.impl!!.parentClassReference!!) - ?: break - - if (!parentClassReferenceSet.add(currentTypeInstance.typeReference)) { - // Cyclic inheritance - compilationUnit.reports += Error( - typeInstance.impl!!.parentClassReference!!.pos, - "Circular inheritance is forbidden", - "Class ${typeInstance.reference.asCASCStyle()} inherits class ${currentTypeInstance.reference.asCASCStyle()} but class ${currentTypeInstance.reference.asCASCStyle()} also inherits class ${typeInstance.reference.asCASCStyle()}" - ) - break - } else creationQueue.add(0, currentTypeInstance.reference) - } + is ClassInstance -> + classQueue += typeInstance.reference + is TraitInstance -> + traitQueue += typeInstance.reference + } + + + if (typeInstance is ClassInstance) { + val parentClassReferenceSet = hashSetOf() + var currentTypeInstance = typeInstance + + while (currentTypeInstance.impl != null && currentTypeInstance.impl!!.parentClassReference != null) { + currentTypeInstance = + Table.findTypeInstance(currentTypeInstance.impl!!.parentClassReference!!) + ?: break + + if (!parentClassReferenceSet.add(currentTypeInstance.typeReference)) { + // Cyclic inheritance + compilationUnit.reports += Error( + typeInstance.impl!!.parentClassReference!!.pos, + "Circular inheritance is forbidden", + "Class ${typeInstance.reference.asCASCStyle()} inherits class ${currentTypeInstance.reference.asCASCStyle()} but class ${currentTypeInstance.reference.asCASCStyle()} also inherits class ${typeInstance.reference.asCASCStyle()}" + ) + break + } else classQueue.add(0, currentTypeInstance.reference) + } + } + + if (typeInstance.traitImpls != null) { + val currentTraitQueue = + LinkedList(typeInstance.traitImpls!!.map(TraitImpl::implementedTraitReference)) + + while (currentTraitQueue.isNotEmpty()) { + val trait = currentTraitQueue.remove() + traitQueue.add(0, trait) + Table.findFile(trait)!!.typeInstance.traitImpls?.map(TraitImpl::implementedTraitReference) + ?.let(currentTraitQueue::addAll) } - is TraitInstance -> {} } } @@ -157,7 +171,14 @@ class Compilation(private val preference: AbstractPreference) { } // Define temporary class through bytecode for ASM library to process (See [getClassLoader][org.casc.lang.asm.CommonClassWriter]) - for (reference in creationQueue.distinct()) { + for (reference in traitQueue.distinct()) { + val cachedFile = Table.findFile(reference)!! + val bytecode = Emitter(preference, true).emit(cachedFile) + + preference.classLoader.defineClass(reference.fullQualifiedPath, bytecode) + } + + for (reference in classQueue.distinct()) { val cachedFile = Table.findFile(reference)!! val bytecode = Emitter(preference, true).emit(cachedFile) diff --git a/src/main/kotlin/org/casc/lang/emitter/Emitter.kt b/src/main/kotlin/org/casc/lang/emitter/Emitter.kt index bb6756a7..ebf24230 100644 --- a/src/main/kotlin/org/casc/lang/emitter/Emitter.kt +++ b/src/main/kotlin/org/casc/lang/emitter/Emitter.kt @@ -66,6 +66,7 @@ class Emitter(private val preference: AbstractPreference, private val declaratio } clazz.impl?.let { emitImpl(classWriter, it) } + clazz.traitImpls?.let { for (traitImpl in it) for (function in traitImpl.functions) emitFunction(classWriter, function) } } private fun emitTrait(classWriter: ClassWriter, trait: TraitInstance) { @@ -74,6 +75,7 @@ class Emitter(private val preference: AbstractPreference, private val declaratio } trait.impl?.let { emitImpl(classWriter, it) } + trait.traitImpls?.let { for (traitImpl in it) for (function in traitImpl.functions) emitFunction(classWriter, function) } } private fun emitImpl(classWriter: ClassWriter, impl: Impl) { diff --git a/src/main/kotlin/org/casc/lang/table/Scope.kt b/src/main/kotlin/org/casc/lang/table/Scope.kt index b84080e3..557889d7 100644 --- a/src/main/kotlin/org/casc/lang/table/Scope.kt +++ b/src/main/kotlin/org/casc/lang/table/Scope.kt @@ -130,7 +130,7 @@ data class Scope( * - if previous step does not return available function signature, search parent class */ fun findSignature( - ownerPath: Reference?, functionName: String, argumentTypes: List, searchTraitOnly: Boolean = false + ownerPath: Reference?, functionName: String, argumentTypes: List, searchTraitOnly: Boolean = false, allowAbstract: Boolean = true ): FunctionSignature? { if (ownerPath == null) return findSignatureInSameType( functionName, argumentTypes @@ -148,41 +148,42 @@ data class Scope( canCast(l, r) } }?.asSignature() - } else { - // TODO: Find functions from implemented traits - var functionSignature = typeInstance.impl?.functions?.find { - it.name?.literal == functionName && it.parameterTypes?.size == argumentTypes.size && argumentTypes.zip( - it.parameterTypes!! - ).all { (l, r) -> - canCast(l, r) - } - }?.let(HasSignature::asSignature) + } + // TODO: Find functions from implemented traits + var functionSignature = typeInstance.impl?.functions?.filter { if (!allowAbstract) !it.abstract else true }?.find { + it.name?.literal == functionName && it.parameterTypes?.size == argumentTypes.size && argumentTypes.zip( + it.parameterTypes!! + ).all { (l, r) -> + canCast(l, r) + } + }?.let(HasSignature::asSignature) - if (functionSignature != null) return functionSignature + if (functionSignature != null) return functionSignature - // Search trait function first - if (typeInstance.traitImpls != null) { - for (traitImpl in typeInstance.traitImpls!!) { - functionSignature = - findSignature(traitImpl.implementedTraitReference, functionName, argumentTypes) + // Search trait function first + if (typeInstance.traitImpls != null) { + for (traitImpl in typeInstance.traitImpls!!) { + functionSignature = + findSignature(traitImpl.implementedTraitReference, functionName, argumentTypes, allowAbstract) - if (functionSignature != null) return functionSignature - } + if (functionSignature != null) return functionSignature } + } - if (searchTraitOnly) return null + if (searchTraitOnly) return null - // Search function in parent class - return if (typeInstance.impl != null) { - findSignature( - typeInstance.impl!!.parentClassReference ?: Reference.OBJECT_TYPE_REFERENCE, - functionName, - argumentTypes - ) - } else null - } + // Search function in parent class + return if (typeInstance.impl != null) { + findSignature( + typeInstance.impl!!.parentClassReference ?: Reference.OBJECT_TYPE_REFERENCE, + functionName, + argumentTypes, + allowAbstract + ) + } else null } + val ownerType = findType(reference) if (ownerType != null) { @@ -215,7 +216,7 @@ data class Scope( ) else retrieveExecutableInfo(ownerType, argTypes) - var functions = filterFunction(ownerClazz, functionName, argumentClasses) + var functions = filterFunction(ownerClazz, functionName, argumentClasses, allowAbstract) for (function in functions) { if (isSameFunction(function, argumentClasses)) { @@ -228,7 +229,7 @@ data class Scope( while (traits.isNotEmpty()) { val trait = traits.remove() - functions = filterFunction(trait, functionName, argumentClasses) + functions = filterFunction(trait, functionName, argumentClasses, allowAbstract) for (function in functions) { if (isSameFunction(function, argumentClasses)) { @@ -246,7 +247,7 @@ data class Scope( ownerClazz = ownerClazz.superclass while (true) { - functions = filterFunction(ownerClazz, functionName, argumentClasses) + functions = filterFunction(ownerClazz, functionName, argumentClasses, allowAbstract) for (function in functions) { if (isSameFunction(function, argumentClasses)) { @@ -266,9 +267,10 @@ data class Scope( private fun filterFunction( ownerClass: Class<*>, functionName: String, - argumentClasses: Array> + argumentClasses: Array>, + allowAbstract: Boolean ): List = - ownerClass.declaredMethods.filter { it.name == functionName && it.parameters.size == argumentClasses.size } + ownerClass.declaredMethods.filter { it.name == functionName && it.parameters.size == argumentClasses.size && if (!allowAbstract) !Modifier.isAbstract(it.modifiers) else true } private fun isSameFunction(function: Method, argumentClasses: Array>): Boolean { for ((i, parameterType) in function.parameterTypes.withIndex()) {