File: vmdynfunc.h

package info (click to toggle)
qtads 2.1.6-1
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 16,156 kB
  • ctags: 18,767
  • sloc: cpp: 133,078; ansic: 26,048; xml: 18; makefile: 11
file content (537 lines) | stat: -rw-r--r-- 18,048 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
/* $Header$ */

/* 
 *   Copyright (c) 2010 Michael J. Roberts.  All Rights Reserved.
 *   
 *   Please see the accompanying license file, LICENSE.TXT, for information
 *   on using and copying this software.  
 */
/*
Name
  vmdynfunc.h - Code Object
Function
  A Code Object is an intrinsic class object that contains a block of byte
  code.  This can be used for things like debugger expressions and run-time
  evaluation and code creation.

  A Code Object is immutable, like a String or List.  It contains the
  original source code text, as a UTF-8 string, and the corresponding
  compiled byte code.
Notes
  
Modified
  12/13/09 MJRoberts  - Creation
*/

#ifndef VMDYNFUNC_H
#define VMDYNFUNC_H

#include "t3std.h"
#include "vmtype.h"
#include "tcprstyp.h"
#include "vmglob.h"
#include "vmobj.h"
#include "vmundo.h"


/* ------------------------------------------------------------------------ */
/*
 *   The image file data block is arranged as follows:
 *   
 *.  UINT2 code_length
 *.  UINT2 obj_ref_cnt
 *.  DATAHOLDER src_string
 *.  BYTE[code_length] bytecode
 *.  UINT2[ref_cnt] obj_refs
 *   
 *   code_length is the size of the bytecode stream, in bytes.
 *   
 *   obj_ref_cnt is the number of object references.  These are stored
 *   immediately after the bytecode stream.  Each reference array entry is a
 *   UINT2 giving the byte offset in the bytecode array of the reference,
 *   which is a UINT4 stored in portable format giving the object ID.
 */


/* ------------------------------------------------------------------------ */
/*
 *   Our in-memory extension data structure, which mimics the image file
 *   structure but uses native types.  
 */

/* prefix header size */
const size_t VMCO_PREFIX_LENGTH = VMB_OBJECT_ID;

/* extension structure */
struct vm_dynfunc_ext
{
    /* allocate the structure */
    static vm_dynfunc_ext *alloc_ext(VMG_ class CVmDynamicFunc *self,
                                     size_t bytecode_len, int obj_ref_cnt);

    /* the source string object */
    vm_val_t src;

    /* 
     *   Method context object.  For a function that's compiled with a local
     *   stack frame context, this is the 'self' object or the complete
     *   method context object (suitable for the LOADCTX opcode), as
     *   applicable, for the enclosing method.  This allows the function to
     *   establish the dynamic enclosing method context at entry.  
     */
    vm_val_t method_ctx;

    /* the size in bytes of the byte code data */
    size_t bytecode_len;

    /* number of object references in the fixup list */
    int obj_ref_cnt;

    /* 
     *   Object references.  The structure is allocated with enough memory
     *   for 'obj_ref_cnt' entries in this array. 
     */
    uint obj_refs[1];

    /*
     *   The structure is allocated with memory following the 'obj_refs'
     *   array for the following dynamic elements:
     *   
     *.   char dynamic_code_header[VMB_OBJECT_ID]
     *.   char bytecode[bytecode_len]
     *   
     *   Explanation of the dynamic object header:
     *   
     *   The VM internally refers to executing code by a pointer directly
     *   into the code's physical memory.  It always keeps an Entry Pointer
     *   value giving a pointer to the first byte of the method; this makes
     *   it easy to get metadata about any active code block by serving as an
     *   identifier for the code.
     *   
     *   Regular code is stored at compile-time in the Code Pool, which is a
     *   special block of read-only memory especially for code.  The FUNCPTR
     *   and CODEPTR primitive datatypes contain offsets into the Code Pool.
     *   A function generated by the compiler is represented by a FUNCPTR
     *   value.  To get a FUNCPTR value given an Entry Pointer, we simply
     *   translate the physical memory pointer back into a Code Pool offset,
     *   and wrap the result in a FUNCPTR value.
     *   
     *   Dynamic code objects are not in the Code Pool, however, since that's
     *   read-only memory that we can't add to at run-time.  Dynamic code
     *   objects are instead allocated in the garbage-collected heap like
     *   other objects.  So we need some way to translate from an Entry
     *   Pointer that points into a dynamic object back into a vm_val_t.  The
     *   vm_val_t representation of a dynamic object is a reference to the
     *   object - i.e., an OBJ value containing the code object's ID.
     *   There's no generic way to look up an object ID given the extension
     *   pointer, which is what the Entry Pointer for a dynamic code object
     *   basically is.  (The Entry Pointer doesn't actually point to the
     *   extension - it points to the start of the 'buf' element of the
     *   extension, because that's where our method header starts.)  So, we
     *   need our own way of working from the Entry Point back to our object
     *   ID.  To do this, we simply add our object ID just ahead of the
     *   method header.  To generate a vm_val_t from an Entry Pointer value,
     *   then, we first look to see if the pointer is a Code Pool element; if
     *   so, we use a FUNCPTR value; if not, it must be a dynamic code
     *   object, so we retrieve the OBJECT_ID value at (Entry Pointer -
     *   VMB_OBJECT_ID) as an object ID, and create an OBJ value with that
     *   ID.  
     */

    /* get a pointer to the dynamic code object method header prefix */
    char *get_prefix_ptr()
    {
        /* the prefix starts after the obj_ref array */
        return (char *)&obj_refs[obj_ref_cnt];
    }

    /* get a pointer to the start of the byte code */
    char *get_bytecode_ptr()
    {
        /* the byte code starts after the dynamic code prefix */
        return get_prefix_ptr() + VMCO_PREFIX_LENGTH;
    }
};


/* ------------------------------------------------------------------------ */
/*
 *   DynamicFunc metaclass 
 */

class CVmDynamicFunc: public CVmObject
{
    friend class CVmMetaclassDynamicFunc;
    friend class CVmDynamicCompiler;
    
public:
    /* metaclass registration object */
    static class CVmMetaclass *metaclass_reg_;
    class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; }

    /* am I of the given metaclass? */
    virtual int is_of_metaclass(class CVmMetaclass *meta) const
    {
        /* try my own metaclass and my base class */
        return (meta == metaclass_reg_
                || CVmObject::is_of_metaclass(meta));
    }

    /* is this a DynamicFunc object? */
    static int is_dynfunc_obj(VMG_ vm_obj_id_t obj)
        { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); }

    /* 
     *   Given a pointer to a method header, retrieve the object ID of the
     *   DynamicFunc that owns the bytecode.  Returns VM_INVALID_OBJ if we
     *   can't find a valid owner.  
     */
    static vm_obj_id_t get_obj_from_prefix(VMG_ const uchar *p);

    /* create from a string, without saving the source text */
    static vm_obj_id_t create(VMG_ int in_root_set,
                              vm_obj_id_t globals, vm_obj_id_t locals,
                              vm_obj_id_t macros,
                              const char *src, size_t src_len)
    {
        vm_val_t v;
        v.set_nil();
        return create(vmg_ in_root_set, globals, locals, macros,
                      &v, src, src_len);
    }

    /* create from a string value */
    static vm_obj_id_t create(VMG_ int in_root_set,
                              vm_obj_id_t globals, vm_obj_id_t locals,
                              vm_obj_id_t macros,
                              const vm_val_t *src);

    /* create from a string, optionally saving the string value */
    static vm_obj_id_t create(VMG_ int in_root_set,
                              vm_obj_id_t symtab, vm_obj_id_t locals,
                              vm_obj_id_t macros,
                              const vm_val_t *src_val,
                              const char *src, size_t src_len);

    /* create dynamically using stack arguments */
    static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr,
                                         uint argc);

    /* notify of deletion */
    void notify_delete(VMG_ int in_root_set);

    /* 
     *   call a static property - we don't have any of our own, so simply
     *   "inherit" the base class handling 
     */
    static int call_stat_prop(VMG_ vm_val_t *result,
                              const uchar **pc_ptr, uint *argc,
                              vm_prop_id_t prop)
    {
        return CVmObject::
            call_stat_prop(vmg_ result, pc_ptr, argc, prop);
    }

    /* reserve constant data */
    virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper,
                                    vm_obj_id_t self)
    {
        /* we cannot be converted to constant data */
    }
    
    /* convert to constant data */
    virtual void convert_to_const_data(VMG_ class CVmConstMapper *,
                                       vm_obj_id_t)
    {
        /* we don't reference anything */
    }

    /* index the object */
    virtual int index_val_q(VMG_ vm_val_t *result,
                            vm_obj_id_t self,
                            const vm_val_t *index_val);

    /* set a property */
    void set_prop(VMG_ class CVmUndo *,
                  vm_obj_id_t, vm_prop_id_t, const vm_val_t *)
    {
        /* we have no settable properties */
        err_throw(VMERR_INVALID_SETPROP);
    }

    /* get a property */
    int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
                 vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc);

    /* invoke */
    int get_invoker(VMG_ vm_val_t *val);

    /* undo operations - we're immutable, so we can ignore these */
    void notify_new_savept() { }
    void apply_undo(VMG_ struct CVmUndoRecord *) { }
    void discard_undo(VMG_ struct CVmUndoRecord *) { }
    void mark_undo_ref(VMG_ struct CVmUndoRecord *) { }

    /* mark our references (we reference our source string) */
    void mark_refs(VMG_ uint);

    /* we don't keep any weak references */
    void remove_stale_weak_refs(VMG0_) { }
    void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { }

    /* load from an image file */
    void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz);

    /* reload from an image file */
    void reload_from_image(VMG_ vm_obj_id_t self,
                           const char *ptr, size_t siz);

    /* rebuild for image file */
    virtual ulong rebuild_image(VMG_ char *buf, ulong buflen);

    /* save to a file */
    void save_to_file(VMG_ class CVmFile *fp);

    /* restore from a file */
    void restore_from_file(VMG_ vm_obj_id_t self,
                           class CVmFile *fp, class CVmObjFixup *fixups);

    /* we're immutable, so we're definitely not changed since loading */
    int is_changed_since_load() const { return FALSE; }

protected:
    /* get my extension data */
    vm_dynfunc_ext *get_ext() const { return (vm_dynfunc_ext *)ext_; }

    /* load or reload image data */
    void load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz);

    /* set the method context object */
    void set_method_ctx(const vm_val_t *val) { get_ext()->method_ctx = *val; }

    /* create a with no initial contents */
    CVmDynamicFunc() { ext_ = 0; }

    /* create with the given byte code array length */
    CVmDynamicFunc(VMG_ vm_obj_id_t self, const vm_val_t *src,
                   size_t bytecode_len, int obj_ref_cnt);

    /* property evaluator - undefined function */
    int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; }

    /* property evaluator - get my source text */
    int getp_get_source(VMG_ vm_obj_id_t, vm_val_t *, uint *);

    /* property evaluation function table */
    static int (CVmDynamicFunc::*func_table_[])(
        VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc);
};


/* ------------------------------------------------------------------------ */
/*
 *   DynamicFunc registration table object 
 */
class CVmMetaclassDynamicFunc: public CVmMetaclass
{
public:
    /* get the global name */
    const char *get_meta_name() const { return "dynamic-func/030000"; }

    /* create from image file */
    void create_for_image_load(VMG_ vm_obj_id_t id)
    {
        new (vmg_ id) CVmDynamicFunc();
        G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE);
    }

    /* create from restoring from saved state */
    void create_for_restore(VMG_ vm_obj_id_t id)
    {
        new (vmg_ id) CVmDynamicFunc();
        G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE);
    }

    /* create dynamically using stack arguments */
    vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc)
        { return CVmDynamicFunc::create_from_stack(vmg_ pc_ptr, argc); }
    
    /* call a static property */
    int call_stat_prop(VMG_ vm_val_t *result,
                       const uchar **pc_ptr, uint *argc,
                       vm_prop_id_t prop)
    {
        return CVmDynamicFunc::call_stat_prop(vmg_ result, pc_ptr, argc, prop);
    }
};

/* ------------------------------------------------------------------------ */
/*
 *   Dynamic compiler interface
 */


/* compilation modes */
enum CVmDynCompMode
{
    /* compile an expression */
    DCModeExpression,

    /* auto-sensing: 'function' syntax, 'method' syntax, or an expression */
    DCModeAuto,

    /* compile a grammar production rule (alternative) list */
    DCModeGramAlt
};

/* debugger expression context */
struct CVmDynCompDebug
{
    CVmDynCompDebug(class CTcPrsDbgSymtab *symtab,
                    tcpn_dyncomp_info &di, int self_valid)
    {
        this->symtab = symtab;
        this->di = di;
        this->self_valid = self_valid;
    }

    /* IN: local symbol table for debugger expression evaluation */
    class CTcPrsDbgSymtab *symtab;

    /* IN: debugger evaluation context settings */
    tcpn_dyncomp_info di;

    /* IN: is there a valid 'self' for this expression? */
    int self_valid;

    /* OUT: the parsed expression is an lvalue */
    int is_lval;
};

/* results structure */
struct CVmDynCompResults
{
    CVmDynCompResults()
    {
        /* clear the error code */
        err = 0;

        /* no error message */
        msgbuf = 0;
    }

    virtual ~CVmDynCompResults()
    {
        free_msgbuf();
    }

    /* Explicitly free the message buffer */
    void free_msgbuf()
    {
        if (msgbuf != 0)
        {
            t3free(msgbuf);
            msgbuf = 0;
        }
    }

    /* throw a dynamic compilation error based on the results */
    void throw_error(VMG0_);

    /* 
     *   In grammar mode (DCModeGramAlt), the compiler passes back the parsed
     *   alt list to the caller by calling this method.  Callers that don't
     *   parse grammar rules can leave this as a no-op.  'alts' is the head
     *   of the alternative list for the parsed grammar rule.  
     */
    virtual void save_grammar(VMG_ class CTcGramProdAlt * /*alts*/,
                              struct CTcGramPropArrows * /*arrows*/) { }
    
    /* compiler error code */
    int err;

    /* 
     *   Message buffer.  The compiler allocates this via t3malloc() if an
     *   error occurs. 
     */
    char *msgbuf;
};

/*
 *   dynamic compiler 
 */
class CVmDynamicCompiler
{
public:
    /* create */
    CVmDynamicCompiler(VMG0_);

    /* destroy */
    ~CVmDynamicCompiler();

    /* 
     *   Get or create the global singleton instance.  This creates an
     *   instance if it doesn't already exist, so that we don't instantiate
     *   the compiler structures until they're actually needed.  Once we
     *   create the object, we keep it around in a global.  
     */
    static CVmDynamicCompiler *get(VMG0_);

    /* 
     *   Compile source code into byte code, returning a new DynamicFunc
     *   instance containing the compiled function.  The return value is the
     *   ID of the new object if the compilation was successful, or
     *   VM_INVALID_OBJ if the compilation failed.
     *   
     *   The source code is the string given by 'src' and 'srclen'.  If
     *   'srcval' is non-null, it should be a VM_SSTR or VM_OBJ value with
     *   the VM representation of the source code string; we'll store this
     *   with the DynamicFunc for retrieval with getSource().  If 'srcval' is
     *   null, the DynamicFunc won't store the source, so getSource() will
     *   return nil.
     *   
     *   'mode' gives the compilation mode, which controls how the source
     *   string is parsed.
     *   
     *   If 'dbg' is non-null, we compile for the debugger, with the options
     *   in 'dbg'.  Debugger evaluation is slightly different from regular
     *   evaluation because it can access local variables from an enclosing
     *   scope.
     *   
     *   If 'errp' is non-null, we'll fill it in with the TCERR_xxx number
     *   for the first compiler error, if any.
     *   
     *   If 'msgbuf' is non-null, we'll fill it in with an allocated string
     *   buffer containing the compiler error message(s).  Multiple messages
     *   are separated by newline '\n' characters.  The caller must free this
     *   buffer with t3free() when done with it.  
     */
    vm_obj_id_t compile(VMG_ int in_root_set,
                        vm_obj_id_t globals, vm_obj_id_t locals,
                        vm_obj_id_t macros,
                        const vm_val_t *srcval,
                        const char *src, size_t srclen,
                        CVmDynCompMode mode, CVmDynCompDebug *dbg,
                        CVmDynCompResults *results);

protected:
    /* generate code for a code body */
    int gen_code_body(
        VMG_ class CTPNStmTop *node, const vm_val_t *srcval,
        CVmDynCompDebug *dbg);

    /* parser */
    class CTcParser *prs_;

    /* compiler host interface */
    class CTcHostIfcDynComp *hostifc_;
};



#endif /* VMDYNFUNC_H */

/*
 *   Register the class 
 */
VM_REGISTER_METACLASS(CVmDynamicFunc)