[RUBY] Wie mrbgem "eingebettet" ist

Dies ist der 15. Artikel des mruby Adventskalenders. Ich war wirklich im Schatten des Grases, weil ich erkältet war ... Bitte vergib mir> <

mruby ist eine sehr einfache API, da Sie "mrb_state *" erstellen und verwenden können, indem Sie "mrb_open ()" für die Verwendung in der C-Sprache aufrufen. Das Folgende ist ein Auszug aus dem von mruby-cli generierten C-Code, aber abgesehen von der Definition von ARGV (auch einige Zeilen) gibt es tatsächlich zwei Zeilen.

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

Andererseits ist mrbgem in diesem mrb_state * auf seltsame Weise in einer "eingebauten" Form, aber es war schwierig, ein Bild zu bekommen. Was für ein Mechanismus ist das also? Ich habe versucht, danach zu jagen, also werde ich darüber sprechen.

In aktuelle Masterversion d9049c10 ist der tatsächliche Status von mrb_open mrb_open_allocf (), was in einigen Fällen eine freie Funktion ist. Es ist eine Funktion, die angegeben werden kann, damit sie ersetzt werden kann. Darin rufen wir "mrb_open_core ()" auf, um mrb_state * zu erstellen.

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); //Funktionen, die integrierte mruby-Klassen usw. initialisieren.

  return mrb;
}

Wenn nach mrb_open_core "DISABLE_GEMS" nicht definiert ist, wird "mrb_init_mrbgems ()" aufgerufen.

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;
}

Dieses mrb_init_mrbgems () ist der Schlüssel. Tatsächlich ist diese Funktion im Quellcode von mruby nicht definiert.

Wo es definiert ist, befindet sich in mruby / build / $ {BUILD_HOST} / mrbgems / gem_init.c, das während des Builds automatisch generiert wird.

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);
}

Wenn jede dieser GENERATED_TMP_ * -Funktionen generiert wird, werden sie in der Mitte des Aufbaus jedes mrbgem erstellt. Wenn beispielsweise "mruby-process" als Abhängigkeit enthalten ist, wird die folgende Datei erstellt.

Der Speicherort ist "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);
}

Sie können sehen, dass wir mit "GENERATED_TMP_mrb_mruby_process_gem_init ()" zwei Dinge tun.

Damit werden sowohl die in C definierte mgem-Implementierung als auch die in Ruby definierte Implementierung geladen. Es ist so, als würden Sie so viele Mgems erstellen, wie Sie benötigen, alle verknüpfen und schließlich Ihr eigenes mrb_open () haben.

Es wird oft gesagt, dass es praktisch ist, einen ein-binären Client mit der Stärke von mruby-cli zu erstellen, aber ich schreibe in Ruby. Ich möchte, dass Sie ein Bild haben, das Sie gerade machen, weil Sie die Verarbeitung bytecodieren und in den Quellcode integrieren können (Sie können dies sofort mit dem Befehl mrbc tun).

(Aus diesem Grund können Mgems, die externe Bibliotheken verwenden, möglicherweise keine Binärdatei ohne statische Verknüpfung selbst verteilen. Nun, es geht nur um statische Verknüpfung ...)


In Bezug auf die Verwendung von mgem, insbesondere für diejenigen, die an CRuby gewöhnt sind, ist die Denkweise ganz anders, daher dachte ich, dass es glücklich wäre, das zu erkennen, also schrieb ich es grob.

Recommended Posts

Wie mrbgem "eingebettet" ist
Wie langsam ist Javas Scanner?
Wie die Website auf dem Bildschirm angezeigt wird