[RUBY] Comment mrbgem est "intégré"

Ceci est le 15e article du calendrier de l'Avent mruby. J'étais vraiment à l'ombre de l'herbe parce que j'avais un rhume ... Veuillez me pardonner> <

mruby est une API très simple car vous pouvez créer et utiliser mrb_state * simplement en appelant mrb_open () pour une utilisation depuis le langage C. Ce qui suit est un extrait du code C généré par mruby-cli, mais à l'exception de la définition d'ARGV (également quelques lignes), il y a en fait deux lignes.

mrb_state *mrb = mrb_open();
// ...
mrb_funcall(mrb, mrb_top_self(mrb), "__main__", 1, ARGV);

D'un autre côté, dans ce mrb_state *, d'une manière étrange, mrbgem est dans une forme "intégrée", mais c'était difficile à imaginer, alors quel genre de mécanisme est-ce? J'ai essayé de le poursuivre, alors je vais en parler.

Dans la version actuelle du maître d9049c10, l'état réel de mrb_open est mrb_open_allocf (), qui est une fonction telle que mallocf () , qui est utilisée dans certains cas / free. C'est une fonction qui peut être spécifiée pour pouvoir être remplacée. Dans celui-ci, nous appelons mrb_open_core ()` pour créer mrb_state *.

MRB_API mrb_state*
mrb_open_core(mrb_allocf f, void *ud)
{
  static const mrb_state mrb_state_zero = { 0 };
  static const struct mrb_context mrb_context_zero = { 0 };
  mrb_state *mrb;

  mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud);
  if (mrb == NULL) return NULL;

  *mrb = mrb_state_zero;
  mrb->allocf_ud = ud;
  mrb->allocf = f;
  mrb->atexit_stack_len = 0;

  mrb_gc_init(mrb, &mrb->gc);
  mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context));
  *mrb->c = mrb_context_zero;
  mrb->root_c = mrb->c;

  mrb_init_core(mrb); //Fonctions qui initialisent les classes intégrées mruby, etc.

  return mrb;
}

Après mrb_open_core, si DISABLE_GEMS n'est pas défini,mrb_init_mrbgems ()sera appelé.

MRB_API mrb_state*
mrb_open_allocf(mrb_allocf f, void *ud)
{
  mrb_state *mrb = mrb_open_core(f, ud);

  if (mrb == NULL) {
    return NULL;
  }

#ifndef DISABLE_GEMS
  mrb_init_mrbgems(mrb);
  mrb_gc_arena_restore(mrb, 0);
#endif
  return mrb;
}

Ce mrb_init_mrbgems () est la clé. En fait, cette fonction n'est pas définie dans le code source de mruby.

Là où il est défini est dans mruby / build / $ {BUILD_HOST} / mrbgems / gem_init.c qui est automatiquement généré lors de la construction.

void
mrb_init_mrbgems(mrb_state *mrb) {
  GENERATED_TMP_mrb_mruby_sprintf_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_print_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_math_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_time_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_struct_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_compar_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_enum_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_string_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_numeric_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_array_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_hash_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_range_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_proc_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_symbol_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_random_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_object_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_objectspace_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_fiber_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_enumerator_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_enum_lazy_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_toplevel_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_error_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_kernel_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_class_ext_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_io_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_process_gem_init(mrb);
  GENERATED_TMP_mrb_mruby_exec_gem_init(mrb);
  mrb_state_atexit(mrb, mrb_final_mrbgems);
}

Lorsque chacune de ces fonctions GENERATED_TMP_ * est générée, c'est qu'elles sont créées au milieu de la construction de chaque mrbgem. Par exemple, si mruby-process est inclus en tant que dépendance, le fichier suivant sera créé.

L'emplacement où il est créé est mruby / build / $ {BUILD_HOST} / mrbgems / mrubi-process / gem_init.c.

/*
 * This file is loading the irep
 * Ruby GEM code.
 *
 * IMPORTANT:
 *   This file was generated!
 *   All manual changes will get lost.
 */
#include <stdlib.h>
#include <mruby.h>
#include <mruby/irep.h>
/* dumped in little endian order.
   use `mrbc -E` option for big endian CPU. */
#include <stdint.h>
extern const uint8_t gem_mrblib_irep_mruby_process[];
const uint8_t
#if defined __GNUC__
__attribute__((aligned(4)))
#elif defined _MSC_VER
__declspec(align(4))
#endif
gem_mrblib_irep_mruby_process[] = {
0x45,0x54,0x49,0x52,0x30,0x30,0x30,0x34,0x6c,0xec,0x00,0x00,0x07,0xd9,0x4d,0x41,
0x54,0x5a,0x30,0x30,0x30,0x30,0x49,0x52,0x45,0x50,0x00,0x00,0x04,0xd8,0x30,0x30,
0x30,0x30,0x00,0x00,0x00,0x40,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x07,
//....
0x00,0x00,0x05,0x00,0x02,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,
0x00,0x45,0x4e,0x44,0x00,0x00,0x00,0x00,0x08,
};
void mrb_mruby_process_gem_init(mrb_state *mrb);
void mrb_mruby_process_gem_final(mrb_state *mrb);

void GENERATED_TMP_mrb_mruby_process_gem_init(mrb_state *mrb) {
  int ai = mrb_gc_arena_save(mrb);
  mrb_mruby_process_gem_init(mrb);
  mrb_load_irep(mrb, gem_mrblib_irep_mruby_process);
  if (mrb->exc) {
    mrb_print_error(mrb);
    exit(EXIT_FAILURE);
  }
  mrb_gc_arena_restore(mrb, ai);
}

void GENERATED_TMP_mrb_mruby_process_gem_final(mrb_state *mrb) {
  mrb_mruby_process_gem_final(mrb);
}

Vous pouvez voir que nous faisons deux choses avec GENERATED_TMP_mrb_mruby_process_gem_init ().

Avec cela, l'implémentation mgem définie en C et l'implémentation définie dans Ruby sont chargées. C'est comme créer autant de mgems dont vous dépendez, les relier tous, et enfin vous avez votre propre mrb_open ().

On dit souvent qu'il est pratique de créer un client à un seul binaire avec la force de mruby-cli, mais ce que je fais, c'est d'écrire en Ruby. Je voudrais que vous ayez l'image que vous faites ainsi parce que vous pouvez coder en octet le traitement et l'incorporer dans le code source (vous pouvez le faire immédiatement en utilisant la commande mrbc).

(C'est pourquoi mgem utilisant une bibliothèque externe peut ne pas être en mesure de distribuer un binaire sans liaison statique par lui-même. Eh bien, tout est question de liaison statique ...)


Concernant l'utilisation de mgem, en particulier pour ceux qui sont habitués à CRuby, la façon de penser est assez différente, alors j'ai pensé qu'il serait heureux de le reconnaître, alors je l'ai écrit grossièrement.

Recommended Posts

Comment mrbgem est "intégré"
Quelle est la lenteur du scanner Java?
Comment le site Web s'affiche à l'écran