Lecture de code CRuby (2): rb_newobj_of

À propos de ce thème

C'est un article que je vais lire l'implémentation de CRuby. C'est la deuxième fois.

Cette fois, je voulais lire autour de rb_intern, qui définit le symbole, mais après avoir creusé quelques fonctions J'étais confus car il y avait une utilisation correcte de la fausse RString et de la vraie RString liée à RString (string). En regardant le code périphérique, il semble que vous puissiez lire que vous comprenez l'opération d'initialisation après avoir sécurisé l'objet Ruby. Par conséquent, je vais me connecter au suivant en lisant rb_newobj_of qui semble sécuriser un nouvel objet.

Parce que le but est de connaître l'état initial de l'objet Je vais lire l'histoire détaillée du GC et de l'attribution en attente.

rb_newobj_of

rb_newobj_of est défini dans gc.c.

VALUE
rb_newobj_of(VALUE klass, VALUE flags)
{
    return newobj_of(klass, flags, 0, 0, 0);
}

Apparemment, l'entité est comme newobj_of. Si vous recherchez le code du côté que vous utilisez et que vous le lisez, klass est une constante qui exprime le type de classe. Dans flags, divers drapeaux liés à l'objet ont été mis sous la forme de bit flags.

À propos, le type de chaque argument et valeur de retour est VALUE. Puisqu'il peut être utilisé comme indicateur de bits, il ne fait aucun doute qu'il s'agit d'un type de nombre naturel, mais de quel type s'agit-il? Et 0 des trois arguments de l'appel est un nombre magique, mais qu'est-ce que cela signifie?

J'ai lu chacun d'eux et l'ai examiné.

Type "VALUE"

Quel est le type VALUE?

J'ai cherché avec grep et j'ai trouvé typedef dans include / ruby / ruby.h.

#if defined HAVE_UINTPTR_T && 0
typedef uintptr_t VALUE;
typedef uintptr_t ID;
# define SIGNED_VALUE intptr_t
# define SIZEOF_VALUE SIZEOF_UINTPTR_T
# undef PRI_VALUE_PREFIX
#elif SIZEOF_LONG == SIZEOF_VOIDP
typedef unsigned long VALUE;
typedef unsigned long ID;
# define SIGNED_VALUE long
# define SIZEOF_VALUE SIZEOF_LONG
# define PRI_VALUE_PREFIX "l"
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
typedef unsigned LONG_LONG VALUE;
typedef unsigned LONG_LONG ID;
# define SIGNED_VALUE LONG_LONG
# define LONG_LONG_VALUE 1
# define SIZEOF_VALUE SIZEOF_LONG_LONG
# define PRI_VALUE_PREFIX PRI_LL_PREFIX
#else
# error ---->> ruby requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<----
#endif

Il ressemble à un type qui correspond à la taille du pointeur de l'environnement. Quel est l'intérêt d'utiliser VALUE au lieu d'utiliser simplement des pointeurs ou des types de nombres naturels?

Si vous regardez l'utilisation de VALUE avec grep, vous pouvez comprendre ce qui suit.

--Basiquement utilisé comme pointeur vers un objet Ruby

Probablement un mécanisme pour améliorer l'efficacité de la mémoire. Maintenant que je sais que c'est essentiellement un indicateur, ça suffit. J'ai décidé d'éviter d'autres recherches sur VALUE pour le moment.

newobj_of

Comme rb_newobj_of, il a une définition dans gc.c.

static VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
{
   :
<<Omission>>
   :
   :
    return obj;
}

Le paramètre 0 spécifié dans rb_newobj_of doit être de v1 à v3.

Lisons dans la tête.

static VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
{
    rb_objspace_t *objspace = &rb_objspace;
    VALUE obj;

    if (UNLIKELY(during_gc || ruby_gc_stressful)) {
	if (during_gc) {
	    dont_gc = 1;
	    during_gc = 0;
	    rb_bug("object allocation during garbage collection phase");
	}

	if (ruby_gc_stressful) {
	    if (!garbage_collect(objspace, FALSE, FALSE, FALSE, GPR_FLAG_NEWOBJ)) {
		rb_memerror();
	    }
	}
    }
   :
<<Omission>>

Oh. «SÛREMENT». ʻUN LIKELY` est spécifié, donc Cette clause conditionnelle est fondamentalement un chemin qui ne se produit pas, alors sautons-la une fois. (Et pour autant que l'instruction conditionnelle soit vue, cela semble être l'opération pendant l'exécution du GC.)

ʻObjspace et ʻobj sont sécurisés dans la déclaration de variable. La macro pour rb_objspace a été définie dans gc.c.

#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
#define rb_objspace (*GET_VM()->objspace)
#else
static rb_objspace_t rb_objspace = {{GC_MALLOC_LIMIT_MIN}};
#endif

Pour autant que vous puissiez voir, cela ressemble à une zone pour placer des objets, comme son nom l'indique. Pour le moment, le but est l'état initial, il semble donc qu'il n'est pas nécessaire de creuser profondément ici.

Passons au milieu de la fonction newobj_of.

static VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
{
<<Omission>>
   :
    obj = heap_get_freeobj(objspace, heap_eden);

    if (RGENGC_CHECK_MODE > 0) assert(BUILTIN_TYPE(obj) == T_NONE);

    /* OBJSETUP */
    RBASIC(obj)->flags = flags & ~FL_WB_PROTECTED;
    RBASIC_SET_CLASS_RAW(obj, klass);
    if (rb_safe_level() >= 3) FL_SET((obj), FL_TAINT);
    RANY(obj)->as.values.v1 = v1;
    RANY(obj)->as.values.v2 = v2;
    RANY(obj)->as.values.v3 = v3;
<<Omission>>
}

Vous pouvez le lire comme si vous aviez acquis freeobj (?) Avec la fonction heap_get_freeobj dans ʻobjque vous avez réservée plus tôt. Après cela, il semble définir desflags pour le résultat de la transmission de ʻobj par la macro RBASIC.

Lorsque FL_WB_PROTECTED est grep,#define FL_WB_PROTECTED (((VALUE) 1) << 5) Vous pouvez voir que c'est un bit flag qui utilise le 5ème bit.

Puisqu'il s'agit d'un produit logique négatif, seul le 5ème bit est ignoré même s'il est spécifié par l'argument flags de newobj_of.

Je me demande qui est le FL_WB_PROTECTED, mais en tant que connaissance de la valeur initiale Je pense qu'il suffit de savoir que la valeur ne tient pas au moment de l'initialisation. C'est parce qu'il devrait y avoir un nom d'indicateur ou une forme d'accès au 5ème bit où vous l'utilisez. (Au contraire, en lisant à partir de maintenant, vous serez en mesure de comprendre la signification de FL_WB_PROTECTED.)

Alors je m'en fiche.

Qu'est-ce que la macro «RBASIC»? Ici, «RANY» est également utilisé d'une manière similaire. Surtout dans «RANY», les arguments «v1» à «v3» sont affectés. Quel genre d'effet cela a-t-il?

Que font «RBASIC_SET_CLASS_RAW» et «FL_SET»? Et quelle heure est-il quand rb_safe_level () vaut 3 ou plus?

--Résumé des questions ici

  1. Que fait heap_get_freeobj?
  2. Que font «RBASIC» et «RANY» à obj?
  1. Que fait «RBASIC_SET_CLASS_RAW»?
  2. Quelle est la situation avec rb_safe_level ()> = 3?

Nous allons jeter un coup d'oeil.

  1. Que fait heap_get_freeobj?

La définition de heap_get_freeobj () est la suivante.

static inline VALUE
heap_get_freeobj(rb_objspace_t *objspace, rb_heap_t *heap)
{
    RVALUE *p = heap->freelist;

    while (1) {
	if (LIKELY(p != NULL)) {
	    heap->freelist = p->as.free.next;
	    return (VALUE)p;
	}
	else {
	    p = heap_get_freeobj_from_next_freepage(objspace, heap);
	}
    }
}

Oh, c'est «PROBABLEMENT». En utilisant «PROBABLE» comme indice, il semble que vous obtenez simplement un objet de la liste gratuite. Cela ne semble pas être lié à l'objectif, alors j'ai juste compris que j'étais capable de sécuriser la mémoire et je suis passé à l'étape suivante.

heap_get_freeobj réserve simplement l'objet de la liste libre

  1. Que font «RBASIC» et «RANY» à obj?

J'ai essayé de grep ce que «RBASIC» et «RANY» font à obj. En conséquence, les deux suivants ont été trouvés.

#define RBASIC(obj)  (R_CAST(RBasic)(obj))
#define RANY(o) ((RVALUE*)(o))

Il y avait la définition suivante pour R_CAST

#define R_CAST(st)   (struct st*)

En d'autres termes, les deux convertissent ʻobj` comme un pointeur vers une certaine structure. (Pour la macro «RBASIC», un pointeur vers la structure «RBasic». Pour RANY, le pointeur vers la structure «RVALUE».)

La structure RBasic et la structure RVALUE étaient les suivantes, respectivement.

struct RBasic {
    VALUE flags;
    const VALUE klass;
}
typedef struct RVALUE {
    union {
	struct {
	    VALUE flags;		/* always 0 for freed obj */
	    struct RVALUE *next;
	} free;
	struct RBasic  basic;
	struct RObject object;
	struct RClass  klass;
	struct RFloat  flonum;
	struct RString string;
	struct RArray  array;
	struct RRegexp regexp;
	struct RHash   hash;
	struct RData   data;
	struct RTypedData   typeddata;
	struct RStruct rstruct;
	struct RBignum bignum;
	struct RFile   file;
	struct RNode   node;
	struct RMatch  match;
	struct RRational rational;
	struct RComplex complex;
	struct RSymbol symbol;
	struct {
	    struct RBasic basic;
	    VALUE v1;
	    VALUE v2;
	    VALUE v3;
	} values;
    } as;
#if GC_DEBUG
    const char *file;
    int line;
#endif
} RVALUE;

Ignorez GC_DEBUG car il semble être des informations de débogage.

RBasic est la première partie de RVALUE. RVALUE semble être un organe communautaire intégré de toutes sortes. Quand je l'ai recherchée, la partie principale de la définition de structure de chaque type inclus dans RVALUE était essentiellement RBasic. À partir de là, on peut voir que RBasic semble être couramment utilisé comme en-tête de gestion d'objets.

Puisque RBasic a des flags, qui est un en-tête commun pour tout l'objet, L'affectation des indicateurs dans le code d'origine était l'attribution d'indicateurs à cet en-tête.

De plus, chaque type inclus dans «RVALUE» a été rendu petit et peut contenir jusqu'à 3 pointeurs (= 3 VALEURS).

Par conséquent, à partir de «v1» dans «valeurs» de «RVALUE» via «v3», En y accédant, il semble que les valeurs de tous les objets puissent être correctement initialisées.

  1. Que fait «RBASIC_SET_CLASS_RAW»?

Vous pouvez trouver ce qui suit par grep.

#define RBASIC_SET_CLASS_RAW(obj, cls) (((struct RBasicRaw *)((VALUE)(obj)))->klass = (cls))

Si vous grep également la définition de la structure RBasicRaw

struct RBasicRaw {
    VALUE flags;
    VALUE klass;
};

C'était comme ça. Il est très similaire à RBasic, mais il semble y avoir une différence dans la propriété const de klass lorsqu'on la compare. Apparemment, toucher la valeur de klass ne devrait pas être fait, Utilisez RBASIC_SET_CLASS_RAW partout où vous en avez besoin Je pense qu'il est protégé pour pouvoir être exploité.

Au fait, la valeur de klass était une constante qui semble indiquer le type de classe. Par conséquent, après tout, RBASIC_SET_CLASS_RAW peut être considéré comme une macro qui enregistre le type ʻobj spécifié comme cls`.

  1. Quelle est la situation avec rb_safe_level ()> = 3?

Enfin, lisons rb_safe_level (). Vous pouvez le trouver au début de safe.c par grep.

int
rb_safe_level(void)
{
    return GET_THREAD()->safe_level;
}

Et GET_THREAD est une fonction en ligne si vous pensez que c'est une macro,

static inline rb_thread_t *
GET_THREAD(void)
{
    rb_thread_t *th = ruby_current_thread;
#if OPT_CALL_CFUNC_WITHOUT_FRAME
    if (UNLIKELY(th->passed_ci != 0)) {
	void vm_call_cfunc_push_frame(rb_thread_t *th);
	vm_call_cfunc_push_frame(th);
    }
#endif
    return th;
}

Comme ça. Est-ce l'acquisition de l'objet thread actuel?

Alors, après avoir regardé jusqu'à ce point, j'ai réalisé: "Oh, n'est-ce pas le niveau de sécurité du modèle de sécurité?" Modèle de sécurité Chaque thread aura un safe_level et sera maillé intuitivement.

J'ai décidé d'arrêter de creuser plus profondément ici et de prioriser si cette compréhension peut interpréter le code original.

Encore une fois au milieu de newobj_of

Je republierai le milieu de newobj_of.

static VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
{
<<Omission>>
   :
    obj = heap_get_freeobj(objspace, heap_eden);

    if (RGENGC_CHECK_MODE > 0) assert(BUILTIN_TYPE(obj) == T_NONE);

    /* OBJSETUP */
    RBASIC(obj)->flags = flags & ~FL_WB_PROTECTED;
    RBASIC_SET_CLASS_RAW(obj, klass);
    if (rb_safe_level() >= 3) FL_SET((obj), FL_TAINT);
    RANY(obj)->as.values.v1 = v1;
    RANY(obj)->as.values.v2 = v2;
    RANY(obj)->as.values.v3 = v3;
<<Omission>>
}

La signification de «FL_TAINT» est surprenante.

À propos, si vous l'avez lu avec la compréhension jusqu'à présent, cette partie est

  1. Sécurisez un nouvel objet
  2. Définissez les indicateurs spécifiés dans l'en-tête de l'objet
  3. Remplacez la classe de l'objet par la classe spécifiée
  4. Si le niveau de sécurité est 3 ou plus, marquez l'objet pour l'état de pollution
  5. Initialisez la partie du corps (v1 à v3) de l'objet à la valeur initiale spécifiée.

Peut être lu comme. Je vois. J'ai l'impression d'avoir une sensation suffisante pour comprendre l'opération d'initialisation.

Donc si vous regardez le reste, vous trouverez des outils pour les assertions et le débogage, Le processus d'enregistrement auprès du garbage collector semblait être différent de quelque chose qui déterminait l'état initial de l'objet.

J'ai décidé que si je n'avais rien d'autre à faire, je pourrais comprendre l'état initial de l'objet.

Conclusion

L'état initial de l'objet sécurisé par rb_newobj_of est le suivant

--La partie d'en-tête de l'objet sera le drapeau avec FL_WB_PROTECTED omis de la spécification des drapeaux et le klass comme spécifié. --Cependant, si le niveau de sécurité est de 3 ou plus, FL_TAINT sera également dans un état permanent. --La partie du corps de l'objet est remise à 0

À propos de la prochaine fois

Cette fois, c'est devenu assez long. La prochaine fois, je veux lire la partie qui me semble facile et la raccourcir.

Les autres informations

Le CRuby que vous lisez dans cet article est le ruby / ruby, branche tronc de github. La valeur de hachage de validation actuelle est c1b05c53b795fdb1137819bc2973d591af2712d0, Je lirai toujours la dernière version à l'avenir.

Recommended Posts

Lecture de code CRuby (2): rb_newobj_of
Lecture de code CRuby (1): PROBABLE / IMPROBABLE
Technologie de lecture du code source (cheet sheet)
Rails 5 Lecture de code Partie 1 ~ Nouvelle méthode ActiveRecord ~