CodegenContext replaced by more specific MethodContext in method generation, mapToCallableMethod call replaced by mapSignature for method header generation
This commit is contained in:
@@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.jet.codegen;
|
package org.jetbrains.jet.codegen;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.jet.codegen.signature.JvmMethodParameterKind;
|
import org.jetbrains.jet.codegen.signature.JvmMethodParameterKind;
|
||||||
import org.jetbrains.jet.codegen.signature.JvmMethodParameterSignature;
|
import org.jetbrains.jet.codegen.signature.JvmMethodParameterSignature;
|
||||||
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor;
|
import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -28,10 +28,10 @@ import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
|
|||||||
public class ConstructorFrameMap extends FrameMap {
|
public class ConstructorFrameMap extends FrameMap {
|
||||||
private int myOuterThisIndex = -1;
|
private int myOuterThisIndex = -1;
|
||||||
|
|
||||||
public ConstructorFrameMap(CallableMethod callableMethod, @Nullable ConstructorDescriptor descriptor) {
|
public ConstructorFrameMap(@NotNull JvmMethodSignature signature) {
|
||||||
enterTemp(OBJECT_TYPE); // this
|
enterTemp(OBJECT_TYPE); // this
|
||||||
|
|
||||||
List<JvmMethodParameterSignature> parameterTypes = callableMethod.getSignature().getKotlinParameterTypes();
|
List<JvmMethodParameterSignature> parameterTypes = signature.getKotlinParameterTypes();
|
||||||
if (parameterTypes != null) {
|
if (parameterTypes != null) {
|
||||||
for (JvmMethodParameterSignature parameterType : parameterTypes) {
|
for (JvmMethodParameterSignature parameterType : parameterTypes) {
|
||||||
if (parameterType.getKind() == JvmMethodParameterKind.OUTER) {
|
if (parameterType.getKind() == JvmMethodParameterKind.OUTER) {
|
||||||
|
|||||||
@@ -74,13 +74,7 @@ public class FunctionCodegen extends GenerationStateAware {
|
|||||||
assert functionDescriptor != null;
|
assert functionDescriptor != null;
|
||||||
|
|
||||||
OwnerKind kind = owner.getContextKind();
|
OwnerKind kind = owner.getContextKind();
|
||||||
JvmMethodSignature method =
|
JvmMethodSignature method = typeMapper.mapSignature(functionDescriptor, true, kind);
|
||||||
typeMapper.mapToCallableMethod(
|
|
||||||
functionDescriptor,
|
|
||||||
false,
|
|
||||||
isCallInsideSameClassAsDeclared(functionDescriptor, owner),
|
|
||||||
isCallInsideSameModuleAsDeclared(functionDescriptor, owner),
|
|
||||||
kind).getSignature();
|
|
||||||
|
|
||||||
if (kind != OwnerKind.TRAIT_IMPL || function.getBodyExpression() != null) {
|
if (kind != OwnerKind.TRAIT_IMPL || function.getBodyExpression() != null) {
|
||||||
boolean needJetAnnotations = kind != OwnerKind.TRAIT_IMPL;
|
boolean needJetAnnotations = kind != OwnerKind.TRAIT_IMPL;
|
||||||
@@ -88,7 +82,7 @@ public class FunctionCodegen extends GenerationStateAware {
|
|||||||
new FunctionGenerationStrategy.FunctionDefault(state, functionDescriptor, function));
|
new FunctionGenerationStrategy.FunctionDefault(state, functionDescriptor, function));
|
||||||
}
|
}
|
||||||
|
|
||||||
generateDefaultIfNeeded(owner.intoFunction(functionDescriptor), state, v, method.getAsmMethod(), functionDescriptor, kind,
|
generateDefaultIfNeeded(owner.intoFunction(functionDescriptor), state, v, method, functionDescriptor, kind,
|
||||||
DefaultParameterValueLoader.DEFAULT);
|
DefaultParameterValueLoader.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,10 +518,10 @@ public class FunctionCodegen extends GenerationStateAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void generateDefaultIfNeeded(
|
static void generateDefaultIfNeeded(
|
||||||
MethodContext owner,
|
@NotNull MethodContext owner,
|
||||||
GenerationState state,
|
GenerationState state,
|
||||||
ClassBuilder v,
|
ClassBuilder v,
|
||||||
Method jvmSignature,
|
JvmMethodSignature signature,
|
||||||
@NotNull FunctionDescriptor functionDescriptor,
|
@NotNull FunctionDescriptor functionDescriptor,
|
||||||
OwnerKind kind,
|
OwnerKind kind,
|
||||||
DefaultParameterValueLoader loadStrategy
|
DefaultParameterValueLoader loadStrategy
|
||||||
@@ -552,6 +546,8 @@ public class FunctionCodegen extends GenerationStateAware {
|
|||||||
CodegenBinding.canHaveOuter(state.getBindingContext(), ((ConstructorDescriptor) functionDescriptor).getContainingDeclaration());
|
CodegenBinding.canHaveOuter(state.getBindingContext(), ((ConstructorDescriptor) functionDescriptor).getContainingDeclaration());
|
||||||
boolean isStatic = isStatic(kind);
|
boolean isStatic = isStatic(kind);
|
||||||
|
|
||||||
|
Method jvmSignature = signature.getAsmMethod();
|
||||||
|
|
||||||
if (kind == OwnerKind.TRAIT_IMPL) {
|
if (kind == OwnerKind.TRAIT_IMPL) {
|
||||||
String correctedDescr = "(" + jvmSignature.getDescriptor().substring(jvmSignature.getDescriptor().indexOf(";") + 1);
|
String correctedDescr = "(" + jvmSignature.getDescriptor().substring(jvmSignature.getDescriptor().indexOf(";") + 1);
|
||||||
jvmSignature = new Method(jvmSignature.getName(), correctedDescr);
|
jvmSignature = new Method(jvmSignature.getName(), correctedDescr);
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ public abstract class FunctionGenerationStrategy<T extends CallableDescriptor> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
protected FrameMap createFrameMap(@NotNull JetTypeMapper typeMapper, @NotNull CodegenContext context) {
|
protected FrameMap createFrameMap(@NotNull JetTypeMapper typeMapper, @NotNull MethodContext context) {
|
||||||
return context.prepareFrame(typeMapper);
|
return context.prepareFrame(typeMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public FrameMap getFrameMap(@NotNull JetTypeMapper typeMapper, @NotNull CodegenContext context) {
|
public FrameMap getFrameMap(@NotNull JetTypeMapper typeMapper, @NotNull MethodContext context) {
|
||||||
if (frameMap == null) {
|
if (frameMap == null) {
|
||||||
frameMap = createFrameMap(typeMapper, context);
|
frameMap = createFrameMap(typeMapper, context);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -749,7 +749,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
});
|
});
|
||||||
|
|
||||||
MethodContext functionContext = context.intoFunction(function);
|
MethodContext functionContext = context.intoFunction(function);
|
||||||
FunctionCodegen.generateDefaultIfNeeded(functionContext, state, v, methodSignature.getAsmMethod(), function, OwnerKind.IMPLEMENTATION,
|
FunctionCodegen.generateDefaultIfNeeded(functionContext, state, v, methodSignature, function, OwnerKind.IMPLEMENTATION,
|
||||||
new DefaultParameterValueLoader() {
|
new DefaultParameterValueLoader() {
|
||||||
@Override
|
@Override
|
||||||
public void putValueOnStack(
|
public void putValueOnStack(
|
||||||
@@ -816,7 +816,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
Method method = typeMapper.mapSignature(bridge).getAsmMethod();
|
Method method = typeMapper.mapSignature(bridge).getAsmMethod();
|
||||||
boolean isConstructor = original instanceof ConstructorDescriptor;
|
boolean isConstructor = original instanceof ConstructorDescriptor;
|
||||||
Method originalMethod = isConstructor ?
|
Method originalMethod = isConstructor ?
|
||||||
typeMapper.mapToCallableMethod((ConstructorDescriptor) original).getSignature().getAsmMethod() :
|
typeMapper.mapConstructorSignature((ConstructorDescriptor) original).getAsmMethod() :
|
||||||
typeMapper.mapSignature(original).getAsmMethod();
|
typeMapper.mapSignature(original).getAsmMethod();
|
||||||
Type[] argTypes = method.getArgumentTypes();
|
Type[] argTypes = method.getArgumentTypes();
|
||||||
|
|
||||||
@@ -938,18 +938,17 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
lookupConstructorExpressionsInClosureIfPresent(constructorContext);
|
lookupConstructorExpressionsInClosureIfPresent(constructorContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
final CallableMethod callableMethod = typeMapper.mapToCallableMethod(constructorDescriptor, context.closure);
|
final JvmMethodSignature constructorSignature = typeMapper.mapConstructorSignature(constructorDescriptor, closure);
|
||||||
JvmMethodSignature constructorMethod = callableMethod.getSignature();
|
|
||||||
|
|
||||||
assert constructorDescriptor != null;
|
assert constructorDescriptor != null;
|
||||||
|
|
||||||
functionCodegen.generateMethod(null, constructorMethod, true, constructorDescriptor, constructorContext,
|
functionCodegen.generateMethod(null, constructorSignature, true, constructorDescriptor, constructorContext,
|
||||||
new FunctionGenerationStrategy.CodegenBased<ConstructorDescriptor>(state, constructorDescriptor) {
|
new FunctionGenerationStrategy.CodegenBased<ConstructorDescriptor>(state, constructorDescriptor) {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
protected FrameMap createFrameMap(@NotNull JetTypeMapper typeMapper, @NotNull CodegenContext context) {
|
protected FrameMap createFrameMap(@NotNull JetTypeMapper typeMapper, @NotNull MethodContext context) {
|
||||||
return new ConstructorFrameMap(callableMethod, callableDescriptor);
|
return new ConstructorFrameMap(constructorSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -959,10 +958,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
FunctionCodegen.generateDefaultIfNeeded(constructorContext, state, v, constructorMethod.getAsmMethod(), constructorDescriptor,
|
FunctionCodegen.generateDefaultIfNeeded(constructorContext, state, v, constructorSignature, constructorDescriptor,
|
||||||
OwnerKind.IMPLEMENTATION, DefaultParameterValueLoader.DEFAULT);
|
OwnerKind.IMPLEMENTATION, DefaultParameterValueLoader.DEFAULT);
|
||||||
|
|
||||||
|
CallableMethod callableMethod = typeMapper.mapToCallableMethod(constructorDescriptor, closure);
|
||||||
FunctionCodegen.generateConstructorWithoutParametersIfNeeded(state, callableMethod, constructorDescriptor, v);
|
FunctionCodegen.generateConstructorWithoutParametersIfNeeded(state, callableMethod, constructorDescriptor, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1336,13 +1335,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
@NotNull FunctionDescriptor inheritedFun,
|
@NotNull FunctionDescriptor inheritedFun,
|
||||||
@NotNull MethodVisitor mv
|
@NotNull MethodVisitor mv
|
||||||
) {
|
) {
|
||||||
JvmMethodSignature jvmSignature = typeMapper.mapToCallableMethod(
|
JvmMethodSignature jvmSignature = typeMapper.mapSignature(inheritedFun, true, OwnerKind.IMPLEMENTATION);
|
||||||
inheritedFun,
|
|
||||||
false,
|
|
||||||
isCallInsideSameClassAsDeclared(inheritedFun, context),
|
|
||||||
isCallInsideSameModuleAsDeclared(inheritedFun, context),
|
|
||||||
OwnerKind.IMPLEMENTATION).getSignature();
|
|
||||||
|
|
||||||
JetMethodAnnotationWriter aw = JetMethodAnnotationWriter.visitAnnotation(mv);
|
JetMethodAnnotationWriter aw = JetMethodAnnotationWriter.visitAnnotation(mv);
|
||||||
int kotlinFlags = getFlagsForVisibility(fun.getVisibility());
|
int kotlinFlags = getFlagsForVisibility(fun.getVisibility());
|
||||||
if (fun instanceof PropertyAccessorDescriptor) {
|
if (fun instanceof PropertyAccessorDescriptor) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import org.jetbrains.asm4.Type;
|
|||||||
import org.jetbrains.asm4.commons.InstructionAdapter;
|
import org.jetbrains.asm4.commons.InstructionAdapter;
|
||||||
import org.jetbrains.asm4.commons.Method;
|
import org.jetbrains.asm4.commons.Method;
|
||||||
import org.jetbrains.jet.codegen.context.CodegenContext;
|
import org.jetbrains.jet.codegen.context.CodegenContext;
|
||||||
|
import org.jetbrains.jet.codegen.context.MethodContext;
|
||||||
import org.jetbrains.jet.codegen.context.ScriptContext;
|
import org.jetbrains.jet.codegen.context.ScriptContext;
|
||||||
import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
|
import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
|
||||||
import org.jetbrains.jet.codegen.state.GenerationState;
|
import org.jetbrains.jet.codegen.state.GenerationState;
|
||||||
@@ -99,7 +100,7 @@ public class ScriptCodegen extends MemberCodegen {
|
|||||||
@NotNull ScriptDescriptor scriptDescriptor,
|
@NotNull ScriptDescriptor scriptDescriptor,
|
||||||
@NotNull ClassDescriptor classDescriptorForScript,
|
@NotNull ClassDescriptor classDescriptorForScript,
|
||||||
@NotNull ClassBuilder classBuilder,
|
@NotNull ClassBuilder classBuilder,
|
||||||
@NotNull CodegenContext context,
|
@NotNull MethodContext context,
|
||||||
@NotNull List<ScriptDescriptor> importedScripts
|
@NotNull List<ScriptDescriptor> importedScripts
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|||||||
@@ -255,15 +255,6 @@ public abstract class CodegenContext {
|
|||||||
|
|
||||||
public abstract boolean isStatic();
|
public abstract boolean isStatic();
|
||||||
|
|
||||||
public void copyAccessors(Map<DeclarationDescriptor, DeclarationDescriptor> accessors) {
|
|
||||||
if (accessors != null) {
|
|
||||||
if (this.accessors == null) {
|
|
||||||
this.accessors = new HashMap<DeclarationDescriptor, DeclarationDescriptor>();
|
|
||||||
}
|
|
||||||
this.accessors.putAll(accessors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void initOuterExpression(JetTypeMapper typeMapper, ClassDescriptor classDescriptor) {
|
protected void initOuterExpression(JetTypeMapper typeMapper, ClassDescriptor classDescriptor) {
|
||||||
ClassDescriptor enclosingClass = getEnclosingClass();
|
ClassDescriptor enclosingClass = getEnclosingClass();
|
||||||
outerExpression = enclosingClass != null && canHaveOuter(typeMapper.getBindingContext(), classDescriptor)
|
outerExpression = enclosingClass != null && canHaveOuter(typeMapper.getBindingContext(), classDescriptor)
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class JvmMethodSignature {
|
|||||||
return kotlinTypeParameter;
|
return kotlinTypeParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@NotNull
|
||||||
public List<JvmMethodParameterSignature> getKotlinParameterTypes() {
|
public List<JvmMethodParameterSignature> getKotlinParameterTypes() {
|
||||||
checkGenericsAvailable();
|
checkGenericsAvailable();
|
||||||
return kotlinParameterTypes;
|
return kotlinParameterTypes;
|
||||||
|
|||||||
@@ -217,13 +217,13 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Type mapType(JetType jetType, @Nullable BothSignatureWriter signatureVisitor, @NotNull JetTypeMapperMode kind) {
|
public Type mapType(@NotNull JetType jetType, @Nullable BothSignatureWriter signatureVisitor, @NotNull JetTypeMapperMode kind) {
|
||||||
return mapType(jetType, signatureVisitor, kind, Variance.INVARIANT);
|
return mapType(jetType, signatureVisitor, kind, Variance.INVARIANT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Type mapType(
|
public Type mapType(
|
||||||
JetType jetType,
|
@NotNull JetType jetType,
|
||||||
@Nullable BothSignatureWriter signatureVisitor,
|
@Nullable BothSignatureWriter signatureVisitor,
|
||||||
@NotNull JetTypeMapperMode kind,
|
@NotNull JetTypeMapperMode kind,
|
||||||
@NotNull Variance howThisTypeIsUsed
|
@NotNull Variance howThisTypeIsUsed
|
||||||
@@ -577,7 +577,7 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private JvmMethodSignature mapSignature(@NotNull FunctionDescriptor f, boolean needGenericSignature, @NotNull OwnerKind kind) {
|
public JvmMethodSignature mapSignature(@NotNull FunctionDescriptor f, boolean needGenericSignature, @NotNull OwnerKind kind) {
|
||||||
String name = f.getName().getName();
|
String name = f.getName().getName();
|
||||||
if (f instanceof PropertyAccessorDescriptor) {
|
if (f instanceof PropertyAccessorDescriptor) {
|
||||||
boolean isGetter = f instanceof PropertyGetterDescriptor;
|
boolean isGetter = f instanceof PropertyGetterDescriptor;
|
||||||
@@ -776,13 +776,19 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
return signatureWriter.makeJvmPropertyAccessorSignature(name, false);
|
return signatureWriter.makeJvmPropertyAccessorSignature(name, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeParameter(BothSignatureWriter signatureWriter, JetType outType) {
|
private void writeParameter(@NotNull BothSignatureWriter signatureWriter, @NotNull JetType outType) {
|
||||||
signatureWriter.writeParameterType(JvmMethodParameterKind.VALUE);
|
signatureWriter.writeParameterType(JvmMethodParameterKind.VALUE);
|
||||||
mapType(outType, signatureWriter, JetTypeMapperMode.VALUE);
|
mapType(outType, signatureWriter, JetTypeMapperMode.VALUE);
|
||||||
signatureWriter.writeParameterTypeEnd();
|
signatureWriter.writeParameterTypeEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
private JvmMethodSignature mapConstructorSignature(ConstructorDescriptor descriptor, CalculatedClosure closure) {
|
@NotNull
|
||||||
|
public JvmMethodSignature mapConstructorSignature(@NotNull ConstructorDescriptor descriptor) {
|
||||||
|
return mapConstructorSignature(descriptor, bindingContext.get(CodegenBinding.CLOSURE, descriptor.getContainingDeclaration()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public JvmMethodSignature mapConstructorSignature(@NotNull ConstructorDescriptor descriptor, @Nullable CalculatedClosure closure) {
|
||||||
|
|
||||||
BothSignatureWriter signatureWriter = new BothSignatureWriter(BothSignatureWriter.Mode.METHOD, true);
|
BothSignatureWriter signatureWriter = new BothSignatureWriter(BothSignatureWriter.Mode.METHOD, true);
|
||||||
|
|
||||||
@@ -848,9 +854,7 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
ConstructorDescriptor superConstructor = (ConstructorDescriptor) superDescriptor;
|
ConstructorDescriptor superConstructor = (ConstructorDescriptor) superDescriptor;
|
||||||
|
|
||||||
if (isObjectLiteral(bindingContext, descriptor.getContainingDeclaration())) {
|
if (isObjectLiteral(bindingContext, descriptor.getContainingDeclaration())) {
|
||||||
//noinspection SuspiciousMethodCalls
|
List<JvmMethodParameterSignature> types = mapConstructorSignature(superConstructor).getKotlinParameterTypes();
|
||||||
CallableMethod superCallable = mapToCallableMethod(superConstructor);
|
|
||||||
List<JvmMethodParameterSignature> types = superCallable.getSignature().getKotlinParameterTypes();
|
|
||||||
if (types != null) {
|
if (types != null) {
|
||||||
for (JvmMethodParameterSignature type : types) {
|
for (JvmMethodParameterSignature type : types) {
|
||||||
signatureWriter.writeParameterType(JvmMethodParameterKind.SUPER_CALL_PARAM);
|
signatureWriter.writeParameterType(JvmMethodParameterKind.SUPER_CALL_PARAM);
|
||||||
@@ -901,11 +905,13 @@ public class JetTypeMapper extends BindingTraceAware {
|
|||||||
return signatureWriter.makeJvmMethodSignature("<init>");
|
return signatureWriter.makeJvmMethodSignature("<init>");
|
||||||
}
|
}
|
||||||
|
|
||||||
public CallableMethod mapToCallableMethod(ConstructorDescriptor descriptor) {
|
@NotNull
|
||||||
|
public CallableMethod mapToCallableMethod(@NotNull ConstructorDescriptor descriptor) {
|
||||||
return mapToCallableMethod(descriptor, bindingContext.get(CodegenBinding.CLOSURE, descriptor.getContainingDeclaration()));
|
return mapToCallableMethod(descriptor, bindingContext.get(CodegenBinding.CLOSURE, descriptor.getContainingDeclaration()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CallableMethod mapToCallableMethod(ConstructorDescriptor descriptor, CalculatedClosure closure) {
|
@NotNull
|
||||||
|
public CallableMethod mapToCallableMethod(@NotNull ConstructorDescriptor descriptor, @Nullable CalculatedClosure closure) {
|
||||||
JvmMethodSignature method = mapConstructorSignature(descriptor, closure);
|
JvmMethodSignature method = mapConstructorSignature(descriptor, closure);
|
||||||
JetType defaultType = descriptor.getContainingDeclaration().getDefaultType();
|
JetType defaultType = descriptor.getContainingDeclaration().getDefaultType();
|
||||||
Type mapped = mapType(defaultType, JetTypeMapperMode.IMPL);
|
Type mapped = mapType(defaultType, JetTypeMapperMode.IMPL);
|
||||||
|
|||||||
Reference in New Issue
Block a user