Mitchell N Charity wrote:
I think this might rephrase as object contains: - object core - properties - customizations of object - customizations of value
- obj core - properties - variable access - value manipulation [ - methods ]
Ok, it sounds like the motivation for separating out "properties" was an implementation concern "(set/getprop are different, depending on the existence of the property hash)". So "properties" is basically just "object core (part 2)". Yes?
It is an optimization. When you look at default.pmc:setprop() there are basically two paths - object has already a property hash or not. These could be optimized by 2 different property vtables.
What is the distinction between "object core" (name, type, ...) and "customizations of object" (get_integer, set_integer, ...) ? Given some function foo, what is a rule/guideline which might tell which table to put it in?
From tt.c: - get_{type} functions call vtable->var.get_{type} - set_{type} functions call vtable->var.set_{type} - other functions call the vtable->val.{method} which calls the get_var_{type} on behalve of them, to get the value - a direct access to e.g. cache.int_val is forbidden - always use a vtable function e.g. "elements" for array size Other methods might belong to the "object core".
Which brings us to var vs val, which I'm afraid I'm still not clear on. Could someone do a one paragraph description?
Getting the value of a tied variable has 2 distinct operations: vtable->var.get_integer (get the variable, doing side effect FETCH code) and then vtable->val.get_intger (getting the value returned from first step).
For plain scalars, this can be done in one step.
Leo - could you repost tt.c, perhaps leaving it inline, rather than using an attachment, to be sure the list archives catch it? Thanks.
Ok inlined here. #include <stddef.h> #include <stdlib.h> #include <stdio.h> #include <malloc.h> /* test program, showing: * * - PMC/Buffer unification * - variable/value vtable split * - tied access to scalars * * compile and run like e.g. so: * * cc -Wall -o tt -g tt.c -Iinclude && ./tt */ /* use current parrot config WRT data types to see the influence * on new structures */ #define PARROT_IN_CORE #include "parrot/config.h" /* UnionVal is the first element of the new Parrot Object aka Pobj */ typedef union UnionVal { struct { /* for 32 bit ints, 64 bit double */ Parrot_Int int_val[2]; /* the 2nd int_val might be useful for */ } i; /* e.g. {nom/denom} floats */ Parrot_Float num_val; /* PMCs UnionVal members */ Parrot_Pointer struct_val; struct STRING* string_val; struct PMC* pmc_val; struct { /* Buffers structure */ void * bufstart; size_t buflen; } b; } UnionVal; /* Parrot Object - base class for all others */ typedef struct Pobj { UnionVal u; Parrot_UInt flags; } Pobj; /* plain Buffer is the smallest Parrot Obj */ typedef struct Buffer { Pobj o; } Buffer; /* a STRING is a derived class, the first element is always a Pobj */ typedef struct String { Pobj o; Parrot_UInt bufused; void *strstart; Parrot_UInt strlen; const void *encoding; const void *type; Parrot_Int language; } STRING; /* some vtable functions (vtable.h) */ typedef void (*init_method_t)(struct PMC* interp, struct PMC* pmc); typedef Parrot_Int (*get_integer_method_t)(struct PMC* interp, struct PMC* pmc); typedef void (*set_integer_method_t)(struct PMC* interp, struct PMC*pmc, Parrot_Int i); typedef struct PMC * (*tie_fetch_method_t)(struct PMC* interp, struct PMC* pmc); typedef void (*tie_store_method_t)(struct PMC* interp, struct PMC* pmc, struct PMC* val); typedef void (*add_method_t)(struct PMC* interp, struct PMC* pmc, struct PMC* value, struct PMC* dest); typedef void (*add_integer_method_t)(struct PMC* interp, struct PMC* pmc, Parrot_Int ival, struct PMC* dest); /* variable access vtable piece */ typedef struct { get_integer_method_t get_integer; set_integer_method_t set_integer; /* ... */ } VAR_VTABLE; /* value access vtable piece */ typedef struct { get_integer_method_t get_integer; set_integer_method_t set_integer; add_integer_method_t add_int; add_method_t add; /* ... */ } VAL_VTABLE; /* tied variable vtable piece */ typedef struct { tie_fetch_method_t tie_fetch; tie_store_method_t tie_store; /* ... */ } TIE_VTABLE; /* VTABLE structure */ typedef struct vtable { Parrot_Int class; Parrot_Int base_class; init_method_t init; /* ... */ /* isa, can, has, name, type and other global things */ VAR_VTABLE var; VAL_VTABLE val; TIE_VTABLE tie; } nVTABLE; /* VTABLE name clash, so nVTABLE */ /* a scalar PMC is a Parrot Object with a VTABLE */ typedef struct PMC { Pobj o; nVTABLE *vtable; } PMC; /* a aggregate or more complex PMC is a Parrot Object with a VTABLE * and a data ptr */ typedef struct APMC { Pobj o; nVTABLE *vtable; void *sync; void *data; void *metadata; void *nextforgc; } APMC; /* defines, to simulate current notation */ #define flags o.flags /* but, as e.g. flags collide all over, these will be replaced * by accessor macros */ #define pobj_set_flag(obj, flag) (obj->flags |= (flag)) #define cache o.u #define bufstart o.u.b.bufstart #define buflen o.u.b.buflen #define int_val i.int_val[0] /* some scalar classes (enums) */ #define SCALAR 1 #define TIED_SCALAR 2 /* forward def */ PMC * pmc_new(struct PMC *interp, Parrot_Int class); /* vtable methods classes/scalar */ void init(struct PMC* interp, PMC *pmc) { /* pmc->cache.int_val = 0; is calloced */ } /* the two and only two functions accessing cache.int_val */ Parrot_Int get_integer(struct PMC* interp, PMC *pmc) { return pmc->cache.int_val; } void set_integer(struct PMC* interp, PMC *pmc, Parrot_Int i) { pmc->cache.int_val = i; } /* this is a invoked user FETCH function incrementing the value */ PMC * tie_fetch(PMC *interp, PMC *pmc) { PMC * p = pmc_new(interp, SCALAR); Parrot_Int i = pmc->vtable->val.get_integer(interp, pmc); i++; p->vtable->val.set_integer(interp, p, i); return p; } void tie_store(PMC *interp, PMC *pmc, PMC *val) { } /* vtable methods classes/tied_scalar */ void set_tied_integer(struct PMC* interp, PMC *pmc, Parrot_Int i) { } /* tied var.get_integer function, calling users FETCH */ Parrot_Int get_tied_integer(struct PMC* interp, PMC *pmc) { PMC *tied_pmc = pmc->vtable->tie.tie_fetch(interp, pmc); Parrot_Int i = tied_pmc->vtable->val.get_integer(interp, tied_pmc); /* set value too, so the value is ok after untieing */ pmc->vtable->val.set_integer(interp, pmc, i); return i; } /* dest = pmc + value type promotion and such missing */ void add(PMC* interp, PMC *pmc, PMC* value, PMC* dest) { int i = pmc->vtable->var.get_integer(interp, pmc); int j = value->vtable->var.get_integer(interp, value); dest->vtable->var.set_integer(interp, dest, i+j); } void add_int(PMC* interp, PMC *pmc, Parrot_Int ival, PMC* dest) { } /* class init plain scalar, generated VTABLE */ nVTABLE scalar_vt = { 1, 1, init, /* isa, can, has, ... */ { get_integer, set_integer }, { get_integer, set_integer, add_int, add }, { 0, 0 } }; #if 0 /* this is the final look of the vtable of a tied scalar */ nVTABLE tied_scalar_vt = { 2, 1, init, /* isa, can, has */ { get_tied_integer, set_tied_integer }, { get_integer, set_integer, add_int, add }, { tie_fetch, tie_store } }; #endif /* various vtable pieces for different types */ VAR_VTABLE tied_var_vt_piece = { get_tied_integer, set_tied_integer }; /* users FETCH, STORE ... functions */ TIE_VTABLE tie_vt_piece = { tie_fetch, tie_store }; void tie(PMC *interp, PMC *pmc) { /* tieing a variable or subclassing or ... * needs a new vtable, which must be subject of memory management * * or constructing a new PMC, cloning data? */ nVTABLE *new_vtable = malloc(sizeof(nVTABLE)); /* copy in old vtable pieces */ memcpy(new_vtable, pmc->vtable, sizeof(nVTABLE)); pmc->vtable = new_vtable; /* set changed parts */ pmc->vtable->var = tied_var_vt_piece; pmc->vtable->tie = tie_vt_piece; pmc->vtable->class = TIED_SCALAR; } void untie(PMC *interp, PMC *pmc) { /* restore standard vtable (pieces) */ pmc->vtable = &scalar_vt; pmc->vtable->class = SCALAR; } /* headers.c, pmc.c */ PMC * pmc_new(struct PMC *interp, Parrot_Int class) { PMC * pmc = calloc(1, sizeof(PMC)); pmc->vtable = &scalar_vt; pmc->vtable->init(interp, pmc); pmc->vtable->class = class; /* base_class ... copy vtable pieces from base_class ... */ return pmc; } /* parrot */ int main() { Parrot_Int i; /* in all function calls the first argument "interp" is * subsituted by 0 below */ PMC *p1 = pmc_new(0, SCALAR); PMC *p2 = pmc_new(0, SCALAR); PMC *p3 = pmc_new(0, SCALAR); Buffer *b = malloc(sizeof(Buffer)); /* buffer access */ b->bufstart = calloc(2, 12); b->buflen = 24; b->flags |= 1<<10; /* print size of various types */ printf("sizeof Parrot_Int \t= %d\n", sizeof(Parrot_Int)); printf("sizeof Parrot_Float \t= %d\n", sizeof(Parrot_Float)); printf("sizeof UnionVal\t= %d\n", sizeof(UnionVal)); printf("sizeof Pobj\t= %d\n", sizeof(Pobj)); printf("sizeof Buffer\t= %d\n", sizeof(Buffer)); printf("sizeof PMC\t= %d\n", sizeof(PMC)); printf("sizeof APMC\t= %d\n", sizeof(APMC)); printf("sizeof STRING\t= %d\n", sizeof(STRING)); printf("\n"); p1->flags |= 1<<10; /* hiding direct flag setting will be the first * step of conversion, followed by unification of flags */ pobj_set_flag(p1, 1); /* plain scalar * set_integer, setting up a macro for a vtable call * wouldn't be the worst idead IMHO */ p1->vtable->var.set_integer(0, p1, 42); i = p1->vtable->var.get_integer(0, p1); printf("get_integer p1:\t" INTVAL_FMT "\n", i); /* tie scalar explicit FETCH increments value */ tie(0, p1 /*, FETCH ... */ ); /* the method call is the same as above, the result differs slightly */ i = p1->vtable->var.get_integer(0, p1); printf("get_inttied p1:\t" INTVAL_FMT "\n", i); /* set p2 */ p2->vtable->var.set_integer(0, p2, 100); /* p3 = p1 + p2 * as p1 is tied and FETCH increments the value of p1, * results are somewhat hmm uncommon */ p1->vtable->val.add(0, p1, p2, p3); i = p3->vtable->var.get_integer(0, p3); printf("get_integer p3:\t" INTVAL_FMT "\n", i); untie(0, p1); /* p1 is a plain untied scalar again */ i = p1->vtable->var.get_integer(0, p1); printf("get_integer p1:\t" INTVAL_FMT "\n", i); /* value is fix after untie */ i = p1->vtable->var.get_integer(0, p1); printf("get_integer p1:\t" INTVAL_FMT "\n", i); return 0; } #if 0 Summary and remarks: - get_{type} functions call vtable->var.get_{type} - set_{type} functions call vtable->var.set_{type} - other functions call the vtable->val.{method} which calls the get_var_{type} on behalve of them, to get the value - a direct access to e.g. cache.int_val is forbidden - always use a vtable function e.g. "elements" for array size - var. vs val methods could be more distinct in prefix - where are current methods: in var or val? - changing VTABLES and GC is unsolved in this test prog Subject to (probably a lot of) changes. And there are still aggregates, references, tainted vars ... #endif /* * Local variables: * c-indentation-style: bsd * c-basic-offset: 4 * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */
Mitchell
leo