1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include <vdb/extern.h>
28 
29 #include "linker-priv.h"
30 #include "schema-parse.h"
31 #include "xform-priv.h"
32 
33 #include <kfs/dyload.h>
34 #include <klib/token.h>
35 #include <klib/symtab.h>
36 #include <klib/symbol.h>
37 #include <klib/out.h>
38 #include <klib/rc.h>
39 #include <sysalloc.h>
40 
41 #include <kfg/config.h>
42 
43 #include <stdlib.h>
44 #include <string.h>
45 #include <byteswap.h>
46 #include <assert.h>
47 
48 #include "transform-functions.h"
49 
50 /* these functions need something to fill in VFuncDesc */
51 static
fake_stub_func(void * self,const VXformInfo * info,int64_t row_id,VRowResult * rslt,uint32_t argc,const VRowData argv[])52 rc_t CC fake_stub_func ( void *self, const VXformInfo *info, int64_t row_id,
53     VRowResult *rslt, uint32_t argc, const VRowData argv [] )
54 {
55     assert(!"THIS FUNCTION IS NEVER TO BE CALLED");
56     abort();
57     return 0;
58 }
59 
60 /* select is REALLY internal */
61 VTRANSFACT_BUILTIN_IMPL ( vdb_select, 1, 0, 0 ) ( const void *self,
62     const VXfactInfo *info, VFuncDesc *rslt, const VFactoryParams *cp, const VFunctionParams *dp )
63 {
64     /* set function pointer to non-NULL */
65     rslt -> u . rf = fake_stub_func;
66     rslt -> variant = vftSelect;
67     return 0;
68 }
69 
70 #if THIS_IS_A_STUB_FOR_COPYPASTA
71 /*
72 function < type T >
73 T passthru #1.0 ( T target )
74      = vdb:stub:function;
75  */
76 /* all pass-through functions are REALLY internal */
77 VTRANSFACT_BUILTIN_IMPL ( vdb_stub_function, 1, 0, 0 ) ( const void *self,
78     const VXfactInfo *info, VFuncDesc *rslt, const VFactoryParams *cp, const VFunctionParams *dp )
79 {
80     bool shouldPassThru = false;
81 
82     /* pass-through functions MUST have exactly one argument */
83     assert(dp->argc == 1);
84 
85     /* This is where to implement logic to determine if pass-through is wanted
86      */
87 
88     /* from here to the end of the function stays the same */
89     if (shouldPassThru) {
90         /* set function pointer to non-NULL */
91         rslt -> u . rf = fake_stub_func;
92 
93         /* this is the magic that causes VFunctionProdRead to pass the Read
94          * message on to the first argument of this function */
95         rslt -> variant = vftPassThrough;
96         return 0;
97     }
98 
99     /* some non-zero RC to signal that pass-through is NOT supposed to happen
100      * this will cause the schema to fall-through to the next rule or fail the
101      * production
102      */
103     return SILENT_RC(rcVDB, rcFunction, rcConstructing, rcFunction, rcIgnored);
104 }
105 #endif
106 
107 /* Pass-through function constructors should use this to prepare the result */
maybePass(bool const shouldPass,VFuncDesc * const rslt)108 static rc_t maybePass(bool const shouldPass, VFuncDesc *const rslt)
109 {
110     if (shouldPass) {
111         rslt -> u . rf = fake_stub_func;
112         rslt -> variant = vftPassThrough;
113         return 0;
114     }
115     return SILENT_RC(rcVDB, rcFunction, rcConstructing, rcFunction, rcIgnored);
116 }
117 
118 /* This function ALWAYS passes through
119 function < type T >
120 T passthru #1.0 ( T target )
121      = vdb:passthru;
122  */
123 /* all pass-through functions are REALLY internal */
124 VTRANSFACT_BUILTIN_IMPL ( vdb_passthru, 1, 0, 0 ) ( const void *self,
125     const VXfactInfo *info, VFuncDesc *rslt, const VFactoryParams *cp, const VFunctionParams *dp )
126 {
127     return maybePass(true, rslt);
128 }
129 
compare_node_value(KConfigNode const * node,unsigned const len,char const * const value)130 static bool compare_node_value(KConfigNode const *node, unsigned const len, char const *const value)
131 {
132     char buffer[4096];
133     size_t num_read = 0, remaining = 0, offset = 0;
134 
135     do {
136         KConfigNodeRead(node, offset, buffer, sizeof(buffer), &num_read, &remaining);
137         if (offset + num_read + remaining != len)
138             return false;
139         assert(offset + num_read <= len);
140         if (memcmp(buffer, value + offset, num_read) != 0)
141             return false;
142         offset += num_read;
143     } while (num_read > 0 && remaining > 0);
144     return offset == len;
145 }
146 
check_config_node(VFactoryParams const * const name_value)147 static bool check_config_node(VFactoryParams const * const name_value)
148 {
149     rc_t rc = 0;
150     bool result = false;
151     assert(name_value->argc == 2);
152     {
153         KConfig *cfg;
154         rc = KConfigMake(&cfg, NULL);
155         if (rc == 0) {
156             KConfigNode const *node;
157             rc = KConfigOpenNodeRead(cfg, &node, "%.*s"
158                                      , (int)name_value->argv[0].count
159                                      , name_value->argv[0].data.ascii);
160             if (rc == 0) {
161                 result = compare_node_value(node
162                                             , name_value->argv[1].count
163                                             , name_value->argv[1].data.ascii);
164                 KConfigNodeRelease(node);
165             }
166             KConfigRelease(cfg);
167         }
168     }
169     return result;
170 }
171 
172 /*
173 function < type T >
174 T is_configuration_set #1.0 < ascii node, ascii value > ( T target )
175      = vdb:is_configuration_set;
176  */
177 /* all pass-through functions are REALLY internal */
178 VTRANSFACT_BUILTIN_IMPL ( vdb_is_configuration_set, 1, 0, 0 ) ( const void *self,
179     const VXfactInfo *info, VFuncDesc *rslt, const VFactoryParams *cp, const VFunctionParams *dp )
180 {
181     return maybePass(check_config_node(cp), rslt);
182 }
183 
184 /* temporary silly stuff
185  */
186 
187 static
hello_func(void * self,const VXformInfo * info,int64_t row_id,VRowResult * rslt,uint32_t argc,const VRowData argv[])188 rc_t CC hello_func ( void *self, const VXformInfo *info, int64_t row_id,
189     VRowResult *rslt, uint32_t argc, const VRowData argv [] )
190 {
191     char *func_hello = self;
192     OUTMSG (( "%s - row id %ld\n", func_hello, row_id ));
193     return 0;
194 }
195 
196 VTRANSFACT_BUILTIN_IMPL ( vdb_hello, 1, 0, 0 ) ( const void *self,
197     const VXfactInfo *info, VFuncDesc *rslt, const VFactoryParams *cp, const VFunctionParams *dp )
198 {
199     const char *fact_hello = "vdb:hello factory";
200     const char *func_hello = "vdb:hello function";
201 
202     if ( cp -> argc > 0 )
203     {
204         fact_hello = cp -> argv [ 0 ] . data . ascii;
205         if ( cp -> argc > 1 )
206             func_hello = cp -> argv [ 1 ] . data . ascii;
207     }
208 
209     rslt -> self = malloc ( strlen ( func_hello ) + 1 );
210     if ( rslt -> self == NULL )
211         return RC ( rcVDB, rcFunction, rcConstructing, rcMemory, rcExhausted );
212     strcpy ( rslt -> self, func_hello );
213     rslt -> whack = free;
214     rslt -> u . rf = hello_func;
215     rslt -> variant = vftRow;
216 
217     OUTMSG (( "%s - %u factory params, %u function params\n", fact_hello, cp -> argc, dp -> argc ));
218     return 0;
219 }
220 
221 /* InitFactories
222  */
223 static
VLinkerEnterFactory(KSymTable * tbl,const SchemaEnv * env,LFactory * lfact,const char * name)224 rc_t CC VLinkerEnterFactory ( KSymTable *tbl, const SchemaEnv *env,
225     LFactory *lfact, const char *name )
226 {
227     rc_t rc;
228 
229     KTokenSource src;
230     KTokenText tt;
231     KToken t;
232 
233     KTokenTextInitCString ( & tt, name, "VLinkerEnterFactory" );
234     KTokenSourceInit ( & src, & tt );
235     next_token ( tbl, & src, & t );
236 
237     rc = create_fqn ( tbl, & src, & t, env, ltFactory, lfact );
238     if ( rc == 0 )
239         lfact -> name = t . sym;
240 
241     return rc;
242 }
243 
244 
VLinkerAddFactories(VLinker * self,const VLinkerIntFactory * fact,uint32_t count,KSymTable * tbl,const SchemaEnv * env)245 rc_t VLinkerAddFactories ( VLinker *self,
246     const VLinkerIntFactory *fact, uint32_t count,
247     KSymTable *tbl, const SchemaEnv *env )
248 {
249     uint32_t i;
250     for ( i = 0; i < count; ++ i )
251     {
252         rc_t rc;
253         LFactory *lfact = malloc ( sizeof * lfact );
254         if ( lfact == NULL )
255             return RC ( rcVDB, rcFunction, rcRegistering, rcMemory, rcExhausted );
256 
257         /* invoke factory to get description */
258         rc = ( * fact [ i ] . f ) ( & lfact -> desc );
259         if ( rc != 0 )
260         {
261             free ( lfact );
262             return rc;
263         }
264 
265         /* I am intrinsic and have no dl symbol */
266         lfact -> addr = NULL;
267         lfact -> name = NULL;
268         lfact -> external = false;
269 
270         /* add to linker */
271         rc = VectorAppend ( & self -> fact, & lfact -> id, lfact );
272         if ( rc != 0 )
273         {
274             LFactoryWhack ( lfact, NULL );
275             return rc;
276         }
277 
278         /* create name */
279         rc = VLinkerEnterFactory ( tbl, env, lfact, fact [ i ] . name );
280         if ( rc != 0 )
281         {
282             void *ignore;
283             VectorSwap ( & self -> fact, lfact -> id, NULL, & ignore );
284             LFactoryWhack ( lfact, NULL );
285             return rc;
286         }
287     }
288 
289     return 0;
290 }
291 
292 
293 static
VLinkerEnterSpecial(KSymTable * tbl,const SchemaEnv * env,LSpecial * lspec,const char * name)294 rc_t CC VLinkerEnterSpecial ( KSymTable *tbl, const SchemaEnv *env,
295     LSpecial *lspec, const char *name )
296 {
297     rc_t rc;
298 
299     KTokenSource src;
300     KTokenText tt;
301     KToken t;
302 
303     KTokenTextInitCString ( & tt, name, "VLinkerEnterSpecial" );
304     KTokenSourceInit ( & src, & tt );
305     next_token ( tbl, & src, & t );
306 
307     rc = create_fqn ( tbl, & src, & t, env, ltUntyped, lspec );
308     if ( rc == 0 )
309         lspec -> name = t . sym;
310 
311     return rc;
312 }
313 
314 static
VLinkerAddUntyped(VLinker * self,const VLinkerIntSpecial * special,uint32_t count,KSymTable * tbl,const SchemaEnv * env)315 rc_t VLinkerAddUntyped ( VLinker *self,
316     const VLinkerIntSpecial *special, uint32_t count,
317     KSymTable *tbl, const SchemaEnv *env )
318 {
319     uint32_t i;
320     for ( i = 0; i < count; ++ i )
321     {
322         rc_t rc;
323         LSpecial *lspec = malloc ( sizeof * lspec );
324         if ( lspec == NULL )
325             return RC ( rcVDB, rcFunction, rcRegistering, rcMemory, rcExhausted );
326 
327         /* I am intrinsic and have no dl symbol */
328         lspec -> addr = NULL;
329         lspec -> name = NULL;
330         lspec -> func = special [ i ] . f;
331 
332         /* add to linker */
333         rc = VectorAppend ( & self -> special, & lspec -> id, lspec );
334         if ( rc != 0 )
335         {
336             LSpecialWhack ( lspec, NULL );
337             return rc;
338         }
339 
340         /* create name */
341         rc = VLinkerEnterSpecial ( tbl, env, lspec, special [ i ] . name );
342         if ( rc != 0 )
343         {
344             void *ignore;
345             VectorSwap ( & self -> special, lspec -> id, NULL, & ignore );
346             LSpecialWhack ( lspec, NULL );
347             return rc;
348         }
349     }
350 
351     return 0;
352 }
353 
354 /* InitFactories
355  */
VLinkerInitFactoriesRead(VLinker * self,KSymTable * tbl,const SchemaEnv * env)356 rc_t VLinkerInitFactoriesRead ( VLinker *self,  KSymTable *tbl, const SchemaEnv *env )
357 {
358     rc_t rc = VLinkerAddFactories ( self, fact, sizeof fact / sizeof fact [ 0 ], tbl, env );
359     if ( rc == 0 )
360         rc = VLinkerAddUntyped ( self, special, sizeof special / sizeof special [ 0 ], tbl, env );
361     return rc;
362 }
363 
364 
365 /* MakeIntrinsic
366  *  creates an initial, intrinsic linker
367  *  pre-loaded with intrinsic factories
368  */
VLinkerMakeIntrinsic(VLinker ** lp)369 rc_t VLinkerMakeIntrinsic ( VLinker **lp )
370 {
371     KDyld *dl;
372     rc_t rc = KDyldMake ( & dl );
373     if ( rc == 0 )
374     {
375         rc = VLinkerMake ( lp, NULL, dl );
376         KDyldRelease ( dl );
377 
378         if ( rc == 0 )
379         {
380             KSymTable tbl;
381             VLinker *self = * lp;
382 
383             /* create symbol table with no intrinsic scope */
384             rc = KSymTableInit ( & tbl, NULL );
385             if ( rc == 0 )
386             {
387                 SchemaEnv env;
388                 SchemaEnvInit ( & env, EXT_SCHEMA_LANG_VERSION );
389 
390                 /* make intrinsic scope modifiable */
391                 KSymTablePushScope ( & tbl, & self -> scope );
392 
393                 /* add intrinsic functions */
394                 rc = VLinkerInitFactories ( self, & tbl, & env );
395                 if ( rc == 0 )
396                 {
397                     KSymTableWhack ( & tbl );
398                     return 0;
399                 }
400 
401                 KSymTableWhack ( & tbl );
402             }
403 
404             VLinkerRelease ( self );
405         }
406     }
407 
408     * lp = NULL;
409 
410     return rc;
411 }
412