Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Variable/value vtable split

11 views
Skip to first unread message

Leopold Toetsch

unread,
Dec 30, 2002, 10:36:31 AM12/30/02
to P6I
<cite author="Dan" date="2002-09-23"
subject="Of variable, values, and vtables">
...
*) Spec the vtable changes
...
The variable/vtable stuff should be done in the next day or two.
</cite>

More threads with $subject:
- Variable/Value split prelims
- [RFC] Buffer/PMC unification, variable/value split, tied scalars

As "the next day or two" is almost gone, I would like to summarize this
thread and maybe start feeding patches WRT this.

- a "variable" consists of 3 parts: name, variable, value
- the 2 latter are now handled in one go in our vtable funcs
- variable and value access should be separate operations (think tie)
- 2-stage access (get_var->value->get/set_xxx) can be avoided for plain
variables by duplicating the value vtable meths to the var vtable.

Things to be considered:
- tied variables
- references
- objects
- aggregates/_keyed access
- ...

Proposal for struct _vtable:
base_vtable
var_vtable
val_vtable
prop_vtable
...

base_vtable ... name, type, ...
var_vtable ... get_integer, set_integer, ...
val_vtable ... add, sub, ...
prop_vtable ... property functions (set/getprop are different, depending
on the existence of the property hash)

The vtable pieces and the _vtable container struct should be pointers to
constant memory, initialized similar to current class_init code.

Proposal for vtable.tbl

[section]
function()
...
[section]
...

First step in changes would be hiding current obj->vtable->meth calls
outside classes by some macros:

CALL_VTABLE_0(meth, interp, pmc)
CALL_VTABLE_1(meth, interp, pmc, arg1)
CALL_VTABLE_2(meth, interp, pmc, arg1, arg2)
...

Comments needed and welcome,
leo

Nicholas Clark

unread,
Dec 30, 2002, 4:36:37 PM12/30/02
to Leopold Toetsch, P6I

Effectively a variable ISA value. So shouldn't val_vtable come first, so
that we can create shorter vtables for values, such as values in aggregates?
Or is this vtable just for variables?

Also, if vtable pieces are in constant memory, how do we efficiently create
singleton objects?

Nicholas Clark

Leopold Toetsch

unread,
Dec 30, 2002, 7:21:40 PM12/30/02
to Nicholas Clark, P6I, Dan Sugalski
Nicholas Clark wrote:

>>base_vtable ... name, type, ...
>>var_vtable ... get_integer, set_integer, ...
>>val_vtable ... add, sub, ...
>>prop_vtable ... property functions (set/getprop are different, depending
>> on the existence of the property hash)
>>
>>The vtable pieces and the _vtable container struct should be pointers to
>>constant memory, initialized similar to current class_init code.

> Effectively a variable ISA value. So shouldn't val_vtable come first, so
> that we can create shorter vtables for values, such as values in aggregates?

A variable "has" or "isa" value and as PMCs are involved there is no
plain value.
Whe we have:
set P0, 5
from this piece of code, we don't know, if we set the value of P0, or if
we set the value of the referenced object, where P0 points to. Only the
vtable knows, what we are assigning to.
So per se, we don't have "values only" in e.g. aggregates, when PMCs are
involved.

The naming of this pieces is of course not fixed. In my test program I
had: var.get_integer and val.get_integer. The former is accessing the
"variable" and - for plain scalars - also getting the value, alas our
current get_integer. For "complex" scalars, the var_vtable just gets a
PMC* and the val_part does the rest.

The order of the vtable pieces is not important. I proposed to use
different vtable pieces for aggregates and scalars, but Dan is against
this "references and the referenced objects should look alike". We will
see when we try to implement references.

> Or is this vtable just for variables?

No - general vtable for everything.
For a more "solid" example, please have a look at my test program tt.c
attached to my 2. mail in the mentioned "[RFC] ..." thread.


> Also, if vtable pieces are in constant memory, how do we efficiently create
> singleton objects?


Objects would have allocated vtable pieces and some header flag noting
this and/or a "overloaded" destroy vtable function. We need probably the
same strategy for overloaded operators.


> Nicholas Clark

leo

Mitchell N Charity

unread,
Dec 30, 2002, 11:13:19 PM12/30/02
to P6I, Leopold Toetsch
On a minor note, re

CALL_VTABLE_0(meth, interp, pmc)
CALL_VTABLE_1(meth, interp, pmc, arg1)
CALL_VTABLE_2(meth, interp, pmc, arg1, arg2)

I suggest reordering arguments to

CALL_VTABLE_1(interp, meth, pmc, arg1)
or CALL_VTABLE_1(interp, pmc, meth, arg1)

Current parrot apis seem to all have the interpreter first, with the
(unfortunate?) exception of some Parrot_jit_* functions, which place
jit_info before it. That gets us to (interp, meth, pmc, arg1).

The choice between (meth, pmc, arg1) and (pmc, meth, arg1) is related
to whether we will find it more natural to think in terms of
a. multimethods (meth.(obj args)), or
b. traditional hierarchical (obj.meth.(args)) or message
passing (obj.(meth args)).

It seems to be simply a human factors choice. The variable order
doesn't affect the macros, and seemingly has little effect on any
code manipulation tools.

A counter argument to all this might be if we expect to someday see a
CALL_VTABLE_METH_1(interp, pmc, arg1) macro, or a similar function,
and wish to minimize dissonance.

But whatever. We can always fiddle later. I wouldn't have mentioned
it if not for the opportunity to flag that the jit functions are using
a perhaps somewhat different argument convention.

Mitchell

Mitchell N Charity

unread,
Dec 30, 2002, 9:39:12 PM12/30/02
to P6I, Leopold Toetsch
Just so everyone doesn't have to go digging through the archives...

Here are links to the postings, and some extracts which seemed to
provide context for the current thread.

http://nntp.x.perl.org/group/perl.perl6.internals/12804
http://nntp.x.perl.org/group/perl.perl6.internals/12809
http://nntp.x.perl.org/group/perl.perl6.internals/13007

We've been kind of sloppy with variables and values so far--not too
surprising as perl 5 was, and many languages (like C) don't make much
of a distinction, and the distinction doesn't much matter even where
it does exist.

We can't do that any more, unfortunately. That's something of a pity,
as it makes things easier, which should've been a clue that we don't
get to do it that way. :)

A "thing" has three parts, a name (which is optional), a container,
and the contents of the container.

The name lives in a symbol table or scratchpad somewhere, and has a
pointer to the container. That's *all* it has, and is otherwise of no
particular use.

The container part is the variable. It holds the value, and is what
mediates assignment. You must go through the variable when storing the
value, and you must go through the variable when fetching the
value. Unfortunately this also means, in the general case, when doing
any access at all of the value in the variable. Luckily in the normal
case we can skip this indirection. Variables also have properties.

The thing in the container is the value. It also has a type, a vtable
that mediates activities involving the value, and properties. Which
are separate from the variable properties. (Which, again, are
separate from attributes)

What does this mean for us?

Well, first it means we need to conceptually split "variables" into
three parts, rather than two as we have been.

It also means that we need to (or at least really should) split
vtables up into parts, so we can pull them upwards as
appropriate. That way we can promote vtable pieces where appropriate,
when the value and variable are of the same type, to cut out dispatch
overhead. And in those cases where we can't do that, we can do the
normal two-level access. (Though hopefully we can avoid it for most
anything besides objects and aggregates)

http://nntp.x.perl.org/group/perl.perl6.internals/13196
http://nntp.x.perl.org/group/perl.perl6.internals/13207

>Do you have a more verbose description of variable/value separation?

Sure. Aggregates are a good one. For example, let's assume you have
an array that can only hold real objects. The objects are the values,
while the array itself is the variable. You *must* go through the
variable's vtable to find a value, while when you manipulate the data
you need to use the vtable in the value.

Tied data's another one. If you tie a scalar, the variable is tied
not the value in the variable. But whenever you access the data in
the variable the variable needs to be involved in the fetch or store,
while the value is what's involved with any actual manipulation.

[...]
Well, with the design as it is, we theoretically need to go through
the variable's vtable every time we need to act on the value in the
variable--we have to do a fetch then dispatch through the fetched
value's vtable to act on it.

What I'd like to be able to do is, for variables that are effectively
passive and don't actually have to get involved, to skip that extra
level of indirection and promote the value's vtable functions into
the variable's vtable.

http://nntp.x.perl.org/group/perl.perl6.internals/13219

So we would have e.g.:

- Plain scalar:
vtable->var.get_integer => return cache.int_val
->val.get_integer => return cache.int_val

- Tied scalar:
vtable->var.get_integer => call tie magic (updating the value) =>
->val.get_integer => return cache.int_val

http://nntp.x.perl.org/group/perl.perl6.internals/13306

Close. (Or the same if I'm misreading) Like:

-tied
vtable->var.get_integer => call tie magic to get value PMC
->tie_magic_returned_pmc.get_integer => reurn cache.int_val

http://nntp.x.perl.org/group/perl.perl6.internals/13314

Your descriptions seems to include another PMC in tie_magic, while mine
just passes the value PMC to tie_magic. tie_magic updates the value,
then the normal scalar behaviour jumps in.

This question does not seem to be resolved in the archive.

http://nntp.x.perl.org/group/perl.perl6.internals/13337

This posting describes a test program attachment, which unfortunately
didn't make it into either archive.

There was a then a two month hiatus, broken by the current thread.

Archivally,
Mitchell

Leopold Toetsch

unread,
Dec 31, 2002, 6:09:40 AM12/31/02
to mcha...@vendian.org, P6I
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

Mitchell N Charity

unread,
Dec 30, 2002, 10:58:03 PM12/30/02
to P6I, Leopold Toetsch
Regards

base_vtable ... name, type, ...
var_vtable ... get_integer, set_integer, ...
val_vtable ... add, sub, ...
prop_vtable ... property functions (set/getprop are different, depending
on the existence of the property hash)

I think this might rephrase as


object contains:
- object core
- properties
- customizations of object
- customizations of value

Currently,
object contains:
- single vtable

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?

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?

Which brings us to var vs val, which I'm afraid I'm still not clear on.


Could someone do a one paragraph description?

(If it is simply a performance hack, what cases are being optimized?
What cases not? What is the before and after flow? What are some
examples of use?)


Leo - could you repost tt.c, perhaps leaving it inline, rather than
using an attachment, to be sure the list archives catch it? Thanks.

Mitchell

Leopold Toetsch

unread,
Dec 31, 2002, 6:14:49 AM12/31/02
to mcha...@vendian.org, P6I
Mitchell N Charity wrote:

> On a minor note, re
>
> CALL_VTABLE_0(meth, interp, pmc)
> CALL_VTABLE_1(meth, interp, pmc, arg1)
> CALL_VTABLE_2(meth, interp, pmc, arg1, arg2)
>
> I suggest reordering arguments to
>
> CALL_VTABLE_1(interp, meth, pmc, arg1)
> or CALL_VTABLE_1(interp, pmc, meth, arg1)
>
> Current parrot apis seem to all have the interpreter first,


As these are accessor macros to certain vtable methods, I'd rather have
the method name first for clarity.

> ... with the


> (unfortunate?) exception of some Parrot_jit_* functions, which place
> jit_info before it.


This is an optimization for e.g. jit/i386, which has one interpreter
already on the stack and thus only the PC has to be pushed for calling
external functions.


> A counter argument to all this might be if we expect to someday see a
> CALL_VTABLE_METH_1(interp, pmc, arg1) macro, or a similar function,
> and wish to minimize dissonance.


Yep, for heavily used function we might have specialized macros.


> Mitchell


leo


Mitchell N Charity

unread,
Dec 31, 2002, 10:42:26 PM12/31/02
to l...@toetsch.at, P6I, Dan Sugalski
I have to be quick, but just to keep things moving along...

(0) Leo writes

Whe we have:
set P0, 5
from this piece of code, we don't know, if we set the value of P0, or if
we set the value of the referenced object, where P0 points to. Only the
vtable knows, what we are assigning to.

(1) This "set" uses core.ops's

inline op set(inout PMC, in INT) {
$1->vtable->set_integer_native(interpreter, $1, $2);
goto NEXT();
}

which becomes core_ops.c's

static opcode_t *
Parrot_assign_p_i (opcode_t *cur_opcode, struct Parrot_Interp * interpreter) {
#line 676 "core.ops"
PREG(1)->vtable->set_integer_native(interpreter, PREG(1), IREG(2));
return (opcode_t *)cur_opcode + 3;
}

(2) Several classes implement set_integer_native.

classes/scalar.pmc has

void set_integer_native (INTVAL value) {
SELF->cache.int_val = value;
}

classes/array.pmc has

void set_integer_native (INTVAL size) {
list_set_length(INTERP, (List *) SELF->data ,size);
}


(3) A new reflector/reference class.

Say we were to create a new classes/reflector.pmc class, with the
intent that operations on the reflector object would be applied to the
object being reflected. An indirection.

Then we might give it a class method

void set_integer_native (INTVAL value) {
PMC *object_reflected = SELF->cache.pmc_val;
object_reflected->vtable->set_integer_native(...);
}

Ok. Let us completely ignore optimization at the moment. Let us
celebrate slowness.

Question ONE - What parts of the above code, if any, need to be
changed, simply and solely to address Dan's concern that we are being
"sloppy with variables and values".

(Based on Dan's comments, I do _not_ expect splitting the vtable into
parts to be part of the answer to this question.)

Mitchell
(Q2 will wait for another time)

Leopold Toetsch

unread,
Jan 5, 2003, 8:56:44 AM1/5/03
to mcha...@vendian.org, P6I, Dan Sugalski
Mitchell N Charity wrote:

> (3) A new reflector/reference class.

> void set_integer_native (INTVAL value) {
> PMC *object_reflected = SELF->cache.pmc_val;
> object_reflected->vtable->set_integer_native(...);
> }

> Question ONE - What parts of the above code, if any, need to be
> changed, simply and solely to address Dan's concern that we are being
> "sloppy with variables and values".


You have now just one vtable method. If your reference should work on
all types you have to implement all functions leading to enormous code
duplication.

Please have a look at tt.c add() - no changes needed if the var-vtable
fetches the variable for you.
So a reflector class would just implement the var.get_<type> and
var.set_<type> functions - that's all.

> Mitchell

leo

0 new messages