@Override
  public void visitObjectLiteralExpression(JetObjectLiteralExpression expression) {
    ClassDescriptor classDescriptor = bindingContext.get(CLASS, expression.getObjectDeclaration());
    if (classDescriptor == null) {
      // working around a problem with shallow analysis
      super.visitObjectLiteralExpression(expression);
      return;
    }

    final String name = inventAnonymousClassName(expression.getObjectDeclaration());
    recordClosure(
        bindingTrace,
        expression.getObjectDeclaration(),
        classDescriptor,
        peekFromStack(classStack),
        JvmClassName.byInternalName(name),
        false);

    classStack.push(classDescriptor);
    //noinspection ConstantConditions
    nameStack.push(bindingContext.get(FQN, classDescriptor).getInternalName());
    super.visitObjectLiteralExpression(expression);
    nameStack.pop();
    classStack.pop();
  }
  @Override
  public void visitObjectDeclaration(JetObjectDeclaration declaration) {
    if (declaration.getParent() instanceof JetObjectLiteralExpression
        || declaration.getParent() instanceof JetClassObject) {
      super.visitObjectDeclaration(declaration);
    } else {
      ClassDescriptor classDescriptor = bindingContext.get(CLASS, declaration);
      // working around a problem with shallow analysis
      if (classDescriptor == null) return;

      String name = getName(classDescriptor);
      recordClosure(
          bindingTrace,
          declaration,
          classDescriptor,
          peekFromStack(classStack),
          JvmClassName.byInternalName(name),
          false);

      classStack.push(classDescriptor);
      nameStack.push(name);
      super.visitObjectDeclaration(declaration);
      nameStack.pop();
      classStack.pop();
    }
  }
  @Override
  public void visitNamedFunction(JetNamedFunction function) {
    FunctionDescriptor functionDescriptor =
        (FunctionDescriptor) bindingContext.get(DECLARATION_TO_DESCRIPTOR, function);
    // working around a problem with shallow analysis
    if (functionDescriptor == null) return;
    DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
    if (containingDeclaration instanceof ClassDescriptor) {
      nameStack.push(peekFromStack(nameStack) + '$' + function.getName());
      super.visitNamedFunction(function);
      nameStack.pop();
    } else if (containingDeclaration instanceof NamespaceDescriptor) {
      String peek = peekFromStack(nameStack);
      if (peek.isEmpty()) {
        peek = JvmAbi.PACKAGE_CLASS;
      } else {
        peek += "/" + JvmAbi.PACKAGE_CLASS;
      }
      nameStack.push(peek + '$' + function.getName());
      super.visitNamedFunction(function);
      nameStack.pop();
    } else {
      String name = inventAnonymousClassName(function);
      ClassDescriptor classDescriptor = recordClassForFunction(functionDescriptor);
      recordClosure(
          bindingTrace,
          function,
          classDescriptor,
          peekFromStack(classStack),
          JvmClassName.byInternalName(name),
          true);

      classStack.push(classDescriptor);
      nameStack.push(name);
      super.visitNamedFunction(function);
      nameStack.pop();
      classStack.pop();
    }
  }
  @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);
    }
  }
  @Override
  public void visitClassObject(JetClassObject classObject) {
    ClassDescriptor classDescriptor = bindingContext.get(CLASS, classObject.getObjectDeclaration());
    assert classDescriptor != null;

    JvmClassName name =
        JvmClassName.byInternalName(peekFromStack(nameStack) + JvmAbi.CLASS_OBJECT_SUFFIX);
    recordClosure(
        bindingTrace, classObject, classDescriptor, peekFromStack(classStack), name, false);

    classStack.push(classDescriptor);
    nameStack.push(name.getInternalName());
    super.visitClassObject(classObject);
    nameStack.pop();
    classStack.pop();
  }
  @Override
  public void visitClass(JetClass klass) {
    ClassDescriptor classDescriptor = bindingContext.get(CLASS, klass);
    // working around a problem with shallow analysis
    if (classDescriptor == null) return;

    String name = getName(classDescriptor);
    recordClosure(
        bindingTrace,
        klass,
        classDescriptor,
        peekFromStack(classStack),
        JvmClassName.byInternalName(name),
        false);

    classStack.push(classDescriptor);
    nameStack.push(name);
    super.visitClass(klass);
    nameStack.pop();
    classStack.pop();
  }
  @Override
  public void visitFunctionLiteralExpression(JetFunctionLiteralExpression expression) {
    FunctionDescriptor functionDescriptor =
        (FunctionDescriptor) bindingContext.get(DECLARATION_TO_DESCRIPTOR, expression);
    // working around a problem with shallow analysis
    if (functionDescriptor == null) return;

    String name = inventAnonymousClassName(expression);
    ClassDescriptor classDescriptor = recordClassForFunction(functionDescriptor);
    recordClosure(
        bindingTrace,
        expression,
        classDescriptor,
        peekFromStack(classStack),
        JvmClassName.byInternalName(name),
        true);

    classStack.push(classDescriptor);
    nameStack.push(name);
    super.visitFunctionLiteralExpression(expression);
    nameStack.pop();
    classStack.pop();
  }
 @Override
 public void visitProperty(JetProperty property) {
   nameStack.push(peekFromStack(nameStack) + '$' + property.getName());
   super.visitProperty(property);
   nameStack.pop();
 }
 @Override
 public void visitJetElement(JetElement element) {
   super.visitJetElement(element);
   element.acceptChildren(this);
 }