Project

General

Profile

Bug #11279

remove rb_control_frame_t::klass

Added by ko1 (Koichi Sasada) about 4 years ago. Updated about 4 years ago.

Status:
Closed
Priority:
Normal
Target version:
-
[ruby-core:69651]

Description

Please check https://bugs.ruby-lang.org/issues/11278
I sent it to ruby-dev because of my operation miss.

This is big change of method data structure.


Related issues

Related to Ruby master - Bug #11278: remove rb_control_frame_t::klassClosedActions

Associated revisions

Revision 5e8a1474
Added by ko1 (Koichi Sasada) about 4 years ago

  • method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions.
    • rb_method_entry(VALUE klass, ID id);
    • rb_method_entry_with_refinements(VALUE klass, ID id);
    • rb_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions.
    • rb_callable_method_entry(VALUE klass, ID id);
    • rb_callable_method_entry_with_refinements(VALUE klass, ID id);
    • rb_callable_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class.
  • method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner.
  • internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS.
  • class.c (method_entry_i): ditto.
  • class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr.
  • gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
  • cont.c (fiber_init): rb_control_frame_t::klass is removed.
  • proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information.
  • vm_core.h: remove several fields.
    • rb_control_frame_t::klass.
    • rb_block_t::klass. And catch up changes.
  • eval.c: catch up changes.
  • gc.c: ditto.
  • insns.def: ditto.
  • vm.c: ditto.
  • vm_args.c: ditto.
  • vm_backtrace.c: ditto.
  • vm_dump.c: ditto.
  • vm_eval.c: ditto.
  • vm_insnhelper.c: ditto.
  • vm_method.c: ditto.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e

Revision 51126
Added by ko1 (Koichi Sasada) about 4 years ago

  • method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions.
    • rb_method_entry(VALUE klass, ID id);
    • rb_method_entry_with_refinements(VALUE klass, ID id);
    • rb_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions.
    • rb_callable_method_entry(VALUE klass, ID id);
    • rb_callable_method_entry_with_refinements(VALUE klass, ID id);
    • rb_callable_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class.
  • method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner.
  • internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS.
  • class.c (method_entry_i): ditto.
  • class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr.
  • gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
  • cont.c (fiber_init): rb_control_frame_t::klass is removed.
  • proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information.
  • vm_core.h: remove several fields.
    • rb_control_frame_t::klass.
    • rb_block_t::klass. And catch up changes.
  • eval.c: catch up changes.
  • gc.c: ditto.
  • insns.def: ditto.
  • vm.c: ditto.
  • vm_args.c: ditto.
  • vm_backtrace.c: ditto.
  • vm_dump.c: ditto.
  • vm_eval.c: ditto.
  • vm_insnhelper.c: ditto.
  • vm_method.c: ditto.

Revision 51126
Added by ko1 (Koichi Sasada) about 4 years ago

  • method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions.
    • rb_method_entry(VALUE klass, ID id);
    • rb_method_entry_with_refinements(VALUE klass, ID id);
    • rb_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions.
    • rb_callable_method_entry(VALUE klass, ID id);
    • rb_callable_method_entry_with_refinements(VALUE klass, ID id);
    • rb_callable_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class.
  • method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner.
  • internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS.
  • class.c (method_entry_i): ditto.
  • class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr.
  • gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
  • cont.c (fiber_init): rb_control_frame_t::klass is removed.
  • proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information.
  • vm_core.h: remove several fields.
    • rb_control_frame_t::klass.
    • rb_block_t::klass. And catch up changes.
  • eval.c: catch up changes.
  • gc.c: ditto.
  • insns.def: ditto.
  • vm.c: ditto.
  • vm_args.c: ditto.
  • vm_backtrace.c: ditto.
  • vm_dump.c: ditto.
  • vm_eval.c: ditto.
  • vm_insnhelper.c: ditto.
  • vm_method.c: ditto.

Revision 51126
Added by ko1 (Koichi Sasada) about 4 years ago

  • method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions.
    • rb_method_entry(VALUE klass, ID id);
    • rb_method_entry_with_refinements(VALUE klass, ID id);
    • rb_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions.
    • rb_callable_method_entry(VALUE klass, ID id);
    • rb_callable_method_entry_with_refinements(VALUE klass, ID id);
    • rb_callable_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class.
  • method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner.
  • internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS.
  • class.c (method_entry_i): ditto.
  • class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr.
  • gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
  • cont.c (fiber_init): rb_control_frame_t::klass is removed.
  • proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information.
  • vm_core.h: remove several fields.
    • rb_control_frame_t::klass.
    • rb_block_t::klass. And catch up changes.
  • eval.c: catch up changes.
  • gc.c: ditto.
  • insns.def: ditto.
  • vm.c: ditto.
  • vm_args.c: ditto.
  • vm_backtrace.c: ditto.
  • vm_dump.c: ditto.
  • vm_eval.c: ditto.
  • vm_insnhelper.c: ditto.
  • vm_method.c: ditto.

Revision 51126
Added by ko1 (Koichi Sasada) about 4 years ago

  • method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions.
    • rb_method_entry(VALUE klass, ID id);
    • rb_method_entry_with_refinements(VALUE klass, ID id);
    • rb_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions.
    • rb_callable_method_entry(VALUE klass, ID id);
    • rb_callable_method_entry_with_refinements(VALUE klass, ID id);
    • rb_callable_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class.
  • method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner.
  • internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS.
  • class.c (method_entry_i): ditto.
  • class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr.
  • gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
  • cont.c (fiber_init): rb_control_frame_t::klass is removed.
  • proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information.
  • vm_core.h: remove several fields.
    • rb_control_frame_t::klass.
    • rb_block_t::klass. And catch up changes.
  • eval.c: catch up changes.
  • gc.c: ditto.
  • insns.def: ditto.
  • vm.c: ditto.
  • vm_args.c: ditto.
  • vm_backtrace.c: ditto.
  • vm_dump.c: ditto.
  • vm_eval.c: ditto.
  • vm_insnhelper.c: ditto.
  • vm_method.c: ditto.

Revision 51126
Added by ko1 (Koichi Sasada) about 4 years ago

  • method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions.
    • rb_method_entry(VALUE klass, ID id);
    • rb_method_entry_with_refinements(VALUE klass, ID id);
    • rb_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions.
    • rb_callable_method_entry(VALUE klass, ID id);
    • rb_callable_method_entry_with_refinements(VALUE klass, ID id);
    • rb_callable_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class.
  • method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner.
  • internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS.
  • class.c (method_entry_i): ditto.
  • class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr.
  • gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
  • cont.c (fiber_init): rb_control_frame_t::klass is removed.
  • proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information.
  • vm_core.h: remove several fields.
    • rb_control_frame_t::klass.
    • rb_block_t::klass. And catch up changes.
  • eval.c: catch up changes.
  • gc.c: ditto.
  • insns.def: ditto.
  • vm.c: ditto.
  • vm_args.c: ditto.
  • vm_backtrace.c: ditto.
  • vm_dump.c: ditto.
  • vm_eval.c: ditto.
  • vm_insnhelper.c: ditto.
  • vm_method.c: ditto.

History

#1

Updated by ko1 (Koichi Sasada) about 4 years ago

  • Related to Bug #11278: remove rb_control_frame_t::klass added
#2

Updated by ko1 (Koichi Sasada) about 4 years ago

  • Status changed from Open to Closed

Applied in changeset r51126.


  • method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions.
    • rb_method_entry(VALUE klass, ID id);
    • rb_method_entry_with_refinements(VALUE klass, ID id);
    • rb_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions.
    • rb_callable_method_entry(VALUE klass, ID id);
    • rb_callable_method_entry_with_refinements(VALUE klass, ID id);
    • rb_callable_method_entry_without_refinements(VALUE klass, ID id);
    • rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class.
  • method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner.
  • internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS.
  • class.c (method_entry_i): ditto.
  • class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr.
  • gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
  • cont.c (fiber_init): rb_control_frame_t::klass is removed.
  • proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information.
  • vm_core.h: remove several fields.
    • rb_control_frame_t::klass.
    • rb_block_t::klass. And catch up changes.
  • eval.c: catch up changes.
  • gc.c: ditto.
  • insns.def: ditto.
  • vm.c: ditto.
  • vm_args.c: ditto.
  • vm_backtrace.c: ditto.
  • vm_dump.c: ditto.
  • vm_eval.c: ditto.
  • vm_insnhelper.c: ditto.
  • vm_method.c: ditto.

Also available in: Atom PDF