private ClassDescriptor recordClassForFunction(FunctionDescriptor funDescriptor) {
    ClassDescriptor classDescriptor;
    int arity = funDescriptor.getValueParameters().size();

    classDescriptor =
        new ClassDescriptorImpl(
            funDescriptor.getContainingDeclaration(),
            Collections.<AnnotationDescriptor>emptyList(),
            Modality.FINAL,
            Name.special("<closure>"));
    ((ClassDescriptorImpl) classDescriptor)
        .initialize(
            false,
            Collections.<TypeParameterDescriptor>emptyList(),
            Collections.singleton(
                (funDescriptor.getReceiverParameter().exists()
                        ? JetStandardClasses.getReceiverFunction(arity)
                        : JetStandardClasses.getFunction(arity))
                    .getDefaultType()),
            JetScope.EMPTY,
            Collections.<ConstructorDescriptor>emptySet(),
            null);

    assert PsiCodegenPredictor.checkPredictedClassNameForFun(
        bindingContext, funDescriptor, classDescriptor);
    bindingTrace.record(CLASS_FOR_FUNCTION, funDescriptor, classDescriptor);
    return classDescriptor;
  }
  public static void registerClassNameForScript(
      BindingTrace bindingTrace,
      @NotNull ScriptDescriptor scriptDescriptor,
      @NotNull JvmClassName className) {
    bindingTrace.record(SCRIPT_NAMES, className);

    ClassDescriptorImpl classDescriptor =
        new ClassDescriptorImpl(
            scriptDescriptor,
            Collections.<AnnotationDescriptor>emptyList(),
            Modality.FINAL,
            Name.special("<script-" + className + ">"));
    classDescriptor.initialize(
        false,
        Collections.<TypeParameterDescriptor>emptyList(),
        Collections.singletonList(KotlinBuiltIns.getInstance().getAnyType()),
        JetScope.EMPTY,
        Collections.<ConstructorDescriptor>emptySet(),
        null,
        false);

    recordClosure(bindingTrace, null, classDescriptor, null, className, false);

    assert PsiCodegenPredictor.checkPredictedClassNameForFun(
        bindingTrace.getBindingContext(), scriptDescriptor, classDescriptor);
    bindingTrace.record(CLASS_FOR_SCRIPT, scriptDescriptor, classDescriptor);
  }
  @NotNull
  public static JvmClassName getJvmInternalName(
      BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor) {
    descriptor = descriptor.getOriginal();
    JvmClassName name = bindingTrace.getBindingContext().get(FQN, descriptor);
    if (name != null) {
      return name;
    }

    name = JvmClassName.byInternalName(getJvmInternalFQNameImpl(bindingTrace, descriptor));

    assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, descriptor, name);
    bindingTrace.record(FQN, descriptor, name);
    return name;
  }
  @Override
  public void visitEnumEntry(JetEnumEntry enumEntry) {
    ClassDescriptor descriptor = bindingContext.get(CLASS, enumEntry);
    assert descriptor != null;

    final boolean trivial = enumEntry.getDeclarations().isEmpty();
    if (!trivial) {
      bindingTrace.record(ENUM_ENTRY_CLASS_NEED_SUBCLASS, descriptor);
      super.visitEnumEntry(enumEntry);
    } else {
      JvmClassName jvmClassName = bindingTrace.get(FQN, peekFromStack(classStack));
      assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, descriptor, jvmClassName);
      bindingTrace.record(FQN, descriptor, jvmClassName);
    }
  }
  static void recordClosure(
      BindingTrace bindingTrace,
      @Nullable JetElement element,
      ClassDescriptor classDescriptor,
      @Nullable ClassDescriptor enclosing,
      JvmClassName name,
      boolean functionLiteral) {
    JetDelegatorToSuperCall superCall = findSuperCall(bindingTrace.getBindingContext(), element);

    CallableDescriptor enclosingReceiver = null;
    if (classDescriptor.getContainingDeclaration() instanceof CallableDescriptor) {
      enclosingReceiver = (CallableDescriptor) classDescriptor.getContainingDeclaration();
      enclosingReceiver =
          enclosingReceiver instanceof PropertyAccessorDescriptor
              ? ((PropertyAccessorDescriptor) enclosingReceiver).getCorrespondingProperty()
              : enclosingReceiver;

      if (enclosingReceiver.getReceiverParameter() == null) {
        enclosingReceiver = null;
      }
    }

    MutableClosure closure = new MutableClosure(superCall, enclosing, enclosingReceiver);

    assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, classDescriptor, name);
    bindingTrace.record(FQN, classDescriptor, name);
    bindingTrace.record(CLOSURE, classDescriptor, closure);

    // TODO: this is temporary before we have proper inner classes
    if (canHaveOuter(bindingTrace.getBindingContext(), classDescriptor) && !functionLiteral) {
      closure.setCaptureThis();
    }

    if (enclosing != null) {
      recordInnerClass(bindingTrace, enclosing, classDescriptor);
    }
  }