LazyDeclarationResolver.kt: convert to Kotlin

This commit is contained in:
Mikhail Glukhikh
2017-09-25 16:55:09 +03:00
parent 04157c4137
commit adf0b25c0b
@@ -14,294 +14,237 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.resolve.lazy; package org.jetbrains.kotlin.resolve.lazy
import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.kotlin.context.GlobalContext
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.context.GlobalContext; import org.jetbrains.kotlin.incremental.KotlinLookupLocation
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.KotlinLookupLocation; import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.incremental.components.LookupLocation; import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation; import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.resolve.lazy.descriptors.AbstractLazyMemberScope
import org.jetbrains.kotlin.renderer.DescriptorRenderer; import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.storage.LockBasedLazyResolveStorageManager
import org.jetbrains.kotlin.resolve.BindingTrace; import javax.inject.Inject
import org.jetbrains.kotlin.resolve.lazy.descriptors.AbstractLazyMemberScope;
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyPackageDescriptor;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
import org.jetbrains.kotlin.storage.LockBasedLazyResolveStorageManager;
import javax.inject.Inject; open class LazyDeclarationResolver @Deprecated("") constructor(
import java.util.List; globalContext: GlobalContext,
delegationTrace: BindingTrace,
private val topLevelDescriptorProvider: TopLevelDescriptorProvider,
private val absentDescriptorHandler: AbsentDescriptorHandler
) {
private val trace: BindingTrace
public class LazyDeclarationResolver { protected lateinit var scopeProvider: DeclarationScopeProvider
@NotNull private final TopLevelDescriptorProvider topLevelDescriptorProvider; private val bindingContext: BindingContext
@NotNull private final AbsentDescriptorHandler absentDescriptorHandler; get() = trace.bindingContext
@NotNull private final BindingTrace trace;
protected DeclarationScopeProvider scopeProvider;
// component dependency cycle // component dependency cycle
@Inject @Inject
public void setDeclarationScopeProvider(@NotNull DeclarationScopeProviderImpl scopeProvider) { fun setDeclarationScopeProvider(scopeProvider: DeclarationScopeProviderImpl) {
this.scopeProvider = scopeProvider; this.scopeProvider = scopeProvider
} }
@Deprecated init {
public LazyDeclarationResolver( val lockBasedLazyResolveStorageManager = LockBasedLazyResolveStorageManager(globalContext.storageManager)
@NotNull GlobalContext globalContext,
@NotNull BindingTrace delegationTrace,
@NotNull TopLevelDescriptorProvider topLevelDescriptorProvider,
@NotNull AbsentDescriptorHandler absentDescriptorHandler
) {
this.topLevelDescriptorProvider = topLevelDescriptorProvider;
this.absentDescriptorHandler = absentDescriptorHandler;
LockBasedLazyResolveStorageManager lockBasedLazyResolveStorageManager =
new LockBasedLazyResolveStorageManager(globalContext.getStorageManager());
this.trace = lockBasedLazyResolveStorageManager.createSafeTrace(delegationTrace); this.trace = lockBasedLazyResolveStorageManager.createSafeTrace(delegationTrace)
} }
@NotNull open fun getClassDescriptor(classOrObject: KtClassOrObject, location: LookupLocation): ClassDescriptor =
public ClassDescriptor getClassDescriptor(@NotNull KtClassOrObject classOrObject, @NotNull LookupLocation location) { findClassDescriptor(classOrObject, location)
return findClassDescriptor(classOrObject, location);
}
@NotNull fun getScriptDescriptor(script: KtScript, location: LookupLocation): ScriptDescriptor =
public ScriptDescriptor getScriptDescriptor(@NotNull KtScript script, @NotNull LookupLocation location) { findClassDescriptor(script, location) as ScriptDescriptor
return (ScriptDescriptor) findClassDescriptor(script, location);
}
@NotNull private fun findClassDescriptor(
private ClassDescriptor findClassDescriptor( classObjectOrScript: KtNamedDeclaration,
@NotNull KtNamedDeclaration classObjectOrScript, location: LookupLocation
@NotNull LookupLocation location ): ClassDescriptor {
) { val scope = getMemberScopeDeclaredIn(classObjectOrScript, location)
MemberScope scope = getMemberScopeDeclaredIn(classObjectOrScript, location);
// Why not use the result here. Because it may be that there is a redeclaration: // Why not use the result here. Because it may be that there is a redeclaration:
// class A {} class A { fun foo(): A<completion here>} // class A {} class A { fun foo(): A<completion here>}
// and if we find the class by name only, we may b-not get the right one. // and if we find the class by name only, we may b-not get the right one.
// This call is only needed to make sure the classes are written to trace // This call is only needed to make sure the classes are written to trace
ClassifierDescriptor scopeDescriptor = scope.getContributedClassifier(classObjectOrScript.getNameAsSafeName(), location); val scopeDescriptor = scope.getContributedClassifier(classObjectOrScript.nameAsSafeName, location)
DeclarationDescriptor descriptor = getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, classObjectOrScript); val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, classObjectOrScript)
if (descriptor == null) { if (descriptor !is ClassDescriptor) {
String providerInfoString = null;
if (scope instanceof AbstractLazyMemberScope) { var providerInfoString: String? = null
AbstractLazyMemberScope lazyMemberScope = (AbstractLazyMemberScope) scope; if (scope is AbstractLazyMemberScope<*, *>) {
providerInfoString = lazyMemberScope.toProviderString(); providerInfoString = scope.toProviderString()
} }
throw new IllegalArgumentException( throw IllegalArgumentException(
String.format( String.format(
"Could not find a classifier for %s.\n" + "Could not find a classifier for %s.\n" +
"Found descriptor: %s (%s).\n" + "Found descriptor: %s (%s).\n" +
"Scope: %s.\n" + "Scope: %s.\n" +
"Provider: %s.", "Provider: %s.",
PsiUtilsKt.getElementTextWithContext(classObjectOrScript), classObjectOrScript.getElementTextWithContext(),
scopeDescriptor != null ? DescriptorRenderer.DEBUG_TEXT.render(scopeDescriptor) : "null", scopeDescriptor?.let { DescriptorRenderer.DEBUG_TEXT.render(it) } ?: "null",
scopeDescriptor != null ? (scopeDescriptor.getContainingDeclaration().getClass()) : null, scopeDescriptor?.containingDeclaration?.javaClass,
scope, scope,
providerInfoString != null ? providerInfoString : "null" providerInfoString ?: "null"
) )
); )
} }
return (ClassDescriptor) descriptor; return descriptor
} }
@NotNull fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor =
private BindingContext getBindingContext() { resolveToDescriptor(declaration, /*track =*/true)
return trace.getBindingContext();
}
@NotNull private fun resolveToDescriptor(declaration: KtDeclaration, track: Boolean): DeclarationDescriptor {
public DeclarationDescriptor resolveToDescriptor(@NotNull KtDeclaration declaration) { return declaration.accept(object : KtVisitor<DeclarationDescriptor?, Nothing?>() {
return resolveToDescriptor(declaration, /*track =*/true); private fun lookupLocationFor(declaration: KtDeclaration, isTopLevel: Boolean): LookupLocation =
} if (isTopLevel && track) KotlinLookupLocation(declaration)
else NoLookupLocation.WHEN_RESOLVE_DECLARATION
@NotNull override fun visitClass(klass: KtClass, data: Nothing?): DeclarationDescriptor =
private DeclarationDescriptor resolveToDescriptor(@NotNull KtDeclaration declaration, boolean track) { getClassDescriptor(klass, lookupLocationFor(klass, klass.isTopLevel()))
DeclarationDescriptor result = declaration.accept(new KtVisitor<DeclarationDescriptor, Void>() {
@NotNull
private LookupLocation lookupLocationFor(@NotNull KtDeclaration declaration, boolean isTopLevel) {
return isTopLevel && track ? new KotlinLookupLocation(declaration) : NoLookupLocation.WHEN_RESOLVE_DECLARATION;
}
@Override override fun visitObjectDeclaration(declaration: KtObjectDeclaration, data: Nothing?): DeclarationDescriptor =
public DeclarationDescriptor visitClass(@NotNull KtClass klass, Void data) { getClassDescriptor(declaration, lookupLocationFor(declaration, declaration.isTopLevel()))
return getClassDescriptor(klass, lookupLocationFor(klass, klass.isTopLevel()));
}
@Override override fun visitTypeParameter(parameter: KtTypeParameter, data: Nothing?): DeclarationDescriptor {
public DeclarationDescriptor visitObjectDeclaration(@NotNull KtObjectDeclaration declaration, Void data) { val ownerElement = PsiTreeUtil.getParentOfType(parameter, KtTypeParameterListOwner::class.java) ?: error("Owner not found for type parameter: " + parameter.text)
return getClassDescriptor(declaration, lookupLocationFor(declaration, declaration.isTopLevel())); val ownerDescriptor = resolveToDescriptor(ownerElement, /*track =*/false)
}
@Override val typeParameters: List<TypeParameterDescriptor>
public DeclarationDescriptor visitTypeParameter(@NotNull KtTypeParameter parameter, Void data) { typeParameters = when (ownerDescriptor) {
KtTypeParameterListOwner ownerElement = PsiTreeUtil.getParentOfType(parameter, KtTypeParameterListOwner.class); is CallableDescriptor -> ownerDescriptor.typeParameters
assert ownerElement != null : "Owner not found for type parameter: " + parameter.getText(); is ClassifierDescriptorWithTypeParameters -> ownerDescriptor.typeConstructor.parameters
DeclarationDescriptor ownerDescriptor = resolveToDescriptor(ownerElement, /*track =*/false); else -> throw IllegalStateException("Unknown owner kind for a type parameter: " + ownerDescriptor)
List<TypeParameterDescriptor> typeParameters;
if (ownerDescriptor instanceof CallableDescriptor) {
CallableDescriptor callableDescriptor = (CallableDescriptor) ownerDescriptor;
typeParameters = callableDescriptor.getTypeParameters();
}
else if (ownerDescriptor instanceof ClassifierDescriptorWithTypeParameters) {
ClassifierDescriptorWithTypeParameters classifierDescriptor = (ClassifierDescriptorWithTypeParameters) ownerDescriptor;
typeParameters = classifierDescriptor.getTypeConstructor().getParameters();
}
else {
throw new IllegalStateException("Unknown owner kind for a type parameter: " + ownerDescriptor);
} }
Name name = parameter.getNameAsSafeName(); val name = parameter.nameAsSafeName
for (TypeParameterDescriptor typeParameterDescriptor : typeParameters) { return typeParameters.firstOrNull { it.name == name }
if (typeParameterDescriptor.getName().equals(name)) { ?: throw IllegalStateException("Type parameter $name not found for $ownerDescriptor")
return typeParameterDescriptor; }
override fun visitNamedFunction(function: KtNamedFunction, data: Nothing?): DeclarationDescriptor? {
val location = lookupLocationFor(function, function.isTopLevel)
val scopeForDeclaration = getMemberScopeDeclaredIn(function, location)
scopeForDeclaration.getContributedFunctions(function.nameAsSafeName, location)
return bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function)
}
override fun visitParameter(parameter: KtParameter, data: Nothing?): DeclarationDescriptor? {
val grandFather = parameter.parent.parent
when (grandFather) {
is KtPrimaryConstructor -> {
val jetClass = grandFather.getContainingClassOrObject()
// This is a primary constructor parameter
val classDescriptor = getClassDescriptor(jetClass, lookupLocationFor(jetClass, false))
return when {
parameter.hasValOrVar() -> {
classDescriptor.defaultType.memberScope.getContributedVariables(
parameter.nameAsSafeName, lookupLocationFor(parameter, false))
bindingContext.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter)
}
else -> {
val constructor = classDescriptor.unsubstitutedPrimaryConstructor
?: error("There are constructor parameters found, so a constructor should also exist")
constructor.valueParameters
bindingContext.get(BindingContext.VALUE_PARAMETER, parameter)
}
}
} }
} is KtNamedFunction -> {
val function = visitNamedFunction(grandFather, data) as? FunctionDescriptor
throw new IllegalStateException("Type parameter " + name + " not found for " + ownerDescriptor); function?.valueParameters
} return bindingContext.get(BindingContext.VALUE_PARAMETER, parameter)
@Override
public DeclarationDescriptor visitNamedFunction(@NotNull KtNamedFunction function, Void data) {
LookupLocation location = lookupLocationFor(function, function.isTopLevel());
MemberScope scopeForDeclaration = getMemberScopeDeclaredIn(function, location);
scopeForDeclaration.getContributedFunctions(function.getNameAsSafeName(), location);
return getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, function);
}
@Override
public DeclarationDescriptor visitParameter(@NotNull KtParameter parameter, Void data) {
PsiElement grandFather = parameter.getParent().getParent();
if (grandFather instanceof KtPrimaryConstructor) {
KtClassOrObject jetClass = ((KtPrimaryConstructor) grandFather).getContainingClassOrObject();
// This is a primary constructor parameter
ClassDescriptor classDescriptor = getClassDescriptor(jetClass, lookupLocationFor(jetClass, false));
if (parameter.hasValOrVar()) {
classDescriptor.getDefaultType().getMemberScope().getContributedVariables(parameter.getNameAsSafeName(), lookupLocationFor(parameter, false));
return getBindingContext().get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter);
} }
else { is KtSecondaryConstructor -> {
ConstructorDescriptor constructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); val constructorDescriptor = visitSecondaryConstructor(
assert constructor != null: "There are constructor parameters found, so a constructor should also exist"; grandFather, data
constructor.getValueParameters(); ) as? ConstructorDescriptor
return getBindingContext().get(BindingContext.VALUE_PARAMETER, parameter); constructorDescriptor?.valueParameters
return bindingContext.get(BindingContext.VALUE_PARAMETER, parameter)
} }
} else -> //TODO: support parameters in accessors and other places(?)
else if (grandFather instanceof KtNamedFunction) { return super.visitParameter(parameter, data)
FunctionDescriptor function = (FunctionDescriptor) visitNamedFunction((KtNamedFunction) grandFather, data);
function.getValueParameters();
return getBindingContext().get(BindingContext.VALUE_PARAMETER, parameter);
}
else if (grandFather instanceof KtSecondaryConstructor) {
ConstructorDescriptor constructorDescriptor = (ConstructorDescriptor) visitSecondaryConstructor(
(KtSecondaryConstructor) grandFather, data
);
constructorDescriptor.getValueParameters();
return getBindingContext().get(BindingContext.VALUE_PARAMETER, parameter);
}
else {
//TODO: support parameters in accessors and other places(?)
return super.visitParameter(parameter, data);
} }
} }
@Override override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor, data: Nothing?): DeclarationDescriptor? {
public DeclarationDescriptor visitSecondaryConstructor(@NotNull KtSecondaryConstructor constructor, Void data) { getClassDescriptor(constructor.parent.parent as KtClassOrObject, lookupLocationFor(constructor, false)).constructors
getClassDescriptor((KtClassOrObject) constructor.getParent().getParent(), lookupLocationFor(constructor, false)).getConstructors(); return bindingContext.get(BindingContext.CONSTRUCTOR, constructor)
return getBindingContext().get(BindingContext.CONSTRUCTOR, constructor);
} }
@Override override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor, data: Nothing?): DeclarationDescriptor? {
public DeclarationDescriptor visitPrimaryConstructor(@NotNull KtPrimaryConstructor constructor, Void data) { getClassDescriptor(constructor.getContainingClassOrObject(), lookupLocationFor(constructor, false)).constructors
getClassDescriptor(constructor.getContainingClassOrObject(), lookupLocationFor(constructor, false)).getConstructors(); return bindingContext.get(BindingContext.CONSTRUCTOR, constructor)
return getBindingContext().get(BindingContext.CONSTRUCTOR, constructor);
} }
@Override override fun visitProperty(property: KtProperty, data: Nothing?): DeclarationDescriptor? {
public DeclarationDescriptor visitProperty(@NotNull KtProperty property, Void data) { val location = lookupLocationFor(property, property.isTopLevel)
LookupLocation location = lookupLocationFor(property, property.isTopLevel()); val scopeForDeclaration = getMemberScopeDeclaredIn(property, location)
MemberScope scopeForDeclaration = getMemberScopeDeclaredIn(property, location); scopeForDeclaration.getContributedVariables(property.nameAsSafeName, location)
scopeForDeclaration.getContributedVariables(property.getNameAsSafeName(), location); return bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, property)
return getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, property);
} }
@Override override fun visitDestructuringDeclarationEntry(
public DeclarationDescriptor visitDestructuringDeclarationEntry( destructuringDeclarationEntry: KtDestructuringDeclarationEntry, data: Nothing?
@NotNull KtDestructuringDeclarationEntry destructuringDeclarationEntry, Void data ): DeclarationDescriptor? {
) { val location = lookupLocationFor(destructuringDeclarationEntry, false)
LookupLocation location = lookupLocationFor(destructuringDeclarationEntry, false); val destructuringDeclaration = destructuringDeclarationEntry.parent as KtDestructuringDeclaration
KtDestructuringDeclaration destructuringDeclaration = ((KtDestructuringDeclaration) destructuringDeclarationEntry.getParent()); val scopeForDeclaration = getMemberScopeDeclaredIn(destructuringDeclaration, location)
MemberScope scopeForDeclaration = getMemberScopeDeclaredIn(destructuringDeclaration, location); scopeForDeclaration.getContributedVariables(destructuringDeclarationEntry.nameAsSafeName, location)
scopeForDeclaration.getContributedVariables(destructuringDeclarationEntry.getNameAsSafeName(), location); return bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, destructuringDeclarationEntry)
return getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, destructuringDeclarationEntry);
} }
@Override override fun visitTypeAlias(typeAlias: KtTypeAlias, data: Nothing?): DeclarationDescriptor? {
public DeclarationDescriptor visitTypeAlias(@NotNull KtTypeAlias typeAlias, Void data) { val location = lookupLocationFor(typeAlias, typeAlias.isTopLevel())
LookupLocation location = lookupLocationFor(typeAlias, typeAlias.isTopLevel()); val scopeForDeclaration = getMemberScopeDeclaredIn(typeAlias, location)
MemberScope scopeForDeclaration = getMemberScopeDeclaredIn(typeAlias, location); scopeForDeclaration.getContributedClassifier(typeAlias.nameAsSafeName, location)
scopeForDeclaration.getContributedClassifier(typeAlias.getNameAsSafeName(), location); return bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, typeAlias)
return getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, typeAlias);
} }
@Override override fun visitScript(script: KtScript, data: Nothing?): DeclarationDescriptor =
public DeclarationDescriptor visitScript(@NotNull KtScript script, Void data) { getScriptDescriptor(script, lookupLocationFor(script, true))
return getScriptDescriptor(script, lookupLocationFor(script, true));
}
@Override override fun visitKtElement(element: KtElement, data: Nothing?): DeclarationDescriptor? {
public DeclarationDescriptor visitKtElement(@NotNull KtElement element, Void data) { throw IllegalArgumentException("Unsupported declaration type: " + element + " " +
throw new IllegalArgumentException("Unsupported declaration type: " + element + " " + element.getElementTextWithContext())
PsiUtilsKt.getElementTextWithContext(element));
} }
}, null); }, null) ?: absentDescriptorHandler.diagnoseDescriptorNotFound(declaration)
if (result == null) {
return absentDescriptorHandler.diagnoseDescriptorNotFound(declaration);
}
return result;
} }
@NotNull internal fun getMemberScopeDeclaredIn(declaration: KtDeclaration, location: LookupLocation):
/*package*/ MemberScope getMemberScopeDeclaredIn(@NotNull KtDeclaration declaration, @NotNull LookupLocation location) { /*package*/ MemberScope {
KtDeclaration parentDeclaration = KtStubbedPsiUtil.getContainingDeclaration(declaration); val parentDeclaration = KtStubbedPsiUtil.getContainingDeclaration(declaration)
boolean isTopLevel = parentDeclaration == null; val isTopLevel = parentDeclaration == null
if (isTopLevel) { // for top level declarations we search directly in package because of possible conflicts with imports if (isTopLevel) { // for top level declarations we search directly in package because of possible conflicts with imports
KtFile ktFile = (KtFile) declaration.getContainingFile(); val ktFile = declaration.containingFile as KtFile
FqName fqName = ktFile.getPackageFqName(); val fqName = ktFile.packageFqName
topLevelDescriptorProvider.assertValid(); topLevelDescriptorProvider.assertValid()
LazyPackageDescriptor packageDescriptor = topLevelDescriptorProvider.getPackageFragment(fqName); val packageDescriptor = topLevelDescriptorProvider.getPackageFragment(fqName)
if (packageDescriptor == null) { if (packageDescriptor == null) {
if (topLevelDescriptorProvider instanceof LazyClassContext) { if (topLevelDescriptorProvider is LazyClassContext) {
((LazyClassContext) topLevelDescriptorProvider).getDeclarationProviderFactory().diagnoseMissingPackageFragment(ktFile); topLevelDescriptorProvider.declarationProviderFactory.diagnoseMissingPackageFragment(ktFile)
} }
else { else {
throw new IllegalStateException("Cannot find package fragment for file " + ktFile.getName() + " with package " + fqName); throw IllegalStateException("Cannot find package fragment for file " + ktFile.name + " with package " + fqName)
} }
} }
return packageDescriptor.getMemberScope(); return packageDescriptor!!.getMemberScope()
} }
else { else {
if (parentDeclaration instanceof KtClassOrObject) { return when (parentDeclaration) {
return getClassDescriptor((KtClassOrObject) parentDeclaration, location).getUnsubstitutedMemberScope(); is KtClassOrObject -> getClassDescriptor(parentDeclaration, location).unsubstitutedMemberScope
} is KtScript -> getScriptDescriptor(parentDeclaration, location).unsubstitutedMemberScope
else if (parentDeclaration instanceof KtScript) { else -> throw IllegalStateException("Don't call this method for local declarations: " + declaration + "\n" +
return getScriptDescriptor((KtScript) parentDeclaration, location).getUnsubstitutedMemberScope(); declaration.getElementTextWithContext())
}
else {
throw new IllegalStateException("Don't call this method for local declarations: " + declaration + "\n" +
PsiUtilsKt.getElementTextWithContext(declaration));
} }
} }
} }