FreeWRL/FreeX3D  3.0.0
JScript_duk.c
1 /****************************************************************************
2  This file is part of the FreeWRL/FreeX3D Distribution.
3 
4  Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
5 
6  FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Lesser Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  FreeWRL/FreeX3D is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
18 ****************************************************************************/
19 
20 /* To do list July 2014
21 - runQueuedDirectOutputs() - is there a way to flag a Script Node so this isn't a double loop over all scripts and fields?
22 - cfwconstructor - fwtype could be extended to articulate allowed AUXTYPEs and FIELDTYPEs for a given W or P
23 To do list Jan 2017
24 - proxy cache could be per-script node to save one lookup loop
25 */
26 
27 
28 #include <config.h>
29 #include <system.h>
30 #if defined(JAVASCRIPT_DUK)
31 #include <display.h>
32 #include <internal.h>
33 
34 #include <libFreeWRL.h>
35 
36 #include "../vrml_parser/Structs.h"
37 #include "../main/headers.h"
38 #include "../vrml_parser/CParseGeneral.h"
39 #include "../vrml_parser/CRoutes.h"
40 #include "../main/Snapshot.h"
41 #include "../scenegraph/Collision.h"
42 #include "../scenegraph/quaternion.h"
43 #include "../scenegraph/Viewer.h"
44 #include "../input/EAIHelpers.h"
45 #include "../input/SensInterps.h"
46 #include "../x3d_parser/Bindable.h"
47 
48 #include "JScript.h"
49 #include "FWTYPE.h"
50 #define FIELDTYPE_MFImage 43
51 typedef int indexT;
52 
53 #ifdef DEBUG_MALLOC
54 #define malloc(A) MALLOCV(A)
55 #define free(A) FREE_IF_NZ(A)
56 #define realloc(A,B) REALLOC(A,B)
57 #endif
58 
59 FWTYPE *fwtypesArray[60]; //true statics - they only need to be defined once per process, we have about 50 types as of july 2014
60 int FWTYPES_COUNT = 0;
61 
62 void initVRMLBrowser(FWTYPE** typeArray, int *n);
63 void initVRMLFields(FWTYPE** typeArray, int *n);
64 void initFWTYPEs(){
65  initVRMLBrowser(fwtypesArray, &FWTYPES_COUNT);
66  initVRMLFields(fwtypesArray, &FWTYPES_COUNT);
67 }
68 FWTYPE *getFWTYPE(int itype){
69  int i;
70  for(i=0;i<FWTYPES_COUNT;i++){
71  if(itype == fwtypesArray[i]->itype)
72  return fwtypesArray[i];
73  }
74  return NULL;
75 }
76 #ifdef _MSC_VER
77 #define strcasecmp _stricmp
78 #endif
79 
80 FWFunctionSpec *getFWFunc(FWTYPE *fwt,const char *key){
81  int i = 0;
82  FWFunctionSpec *fs = fwt->Functions;
83  if(fs)
84  while(fs[i].name){
85  if(!strcasecmp(fs[i].name,key)){
86  //found it - its a function, return functionSpec
87  return &fs[i];
88  }
89  i++;
90  }
91  return NULL;
92 }
93 FWPropertySpec *getFWProp(FWTYPE *fwt,const char *key, int *index){
94  int i = 0;
95  FWPropertySpec *ps = fwt->Properties;
96  *index = 0;
97  if(ps)
98  while(ps[i].name){
99  if(!strcasecmp(ps[i].name,key)){
100  //found it - its a property, return propertySpec
101  (*index) = ps[i].index; //index can be any +- integer
102  return &ps[i];
103  }
104  i++;
105  }
106  return NULL;
107 }
108 int len_properties(FWPropertySpec *ps){
109  int len = 0;
110  if(ps) while(ps[len].name) len++;
111  return len;
112 }
113 int len_functions(FWFunctionSpec *fs){
114  int len = 0;
115  if(fs) while(fs[len].name) len++;
116  return len;
117 }
118 int fwiterator_generic(int index, FWTYPE *fwt, void *pointer, const char **name, int *lastProp, int *jndex, char *type, char *readOnly){
119  //start iterating by passing -1 for index. When you get -1 back, you are done.
120  //FWPointer is for SFNode: it will have an instance-specific result from its custom iterator
121  //next property
122  int lenp, lenf, ifindex;
123  FWPropertySpec *ps;
124  FWIterator iterator;
125  FWFunctionSpec *fs;
126  (*jndex) = 0;
127  ps = fwt->Properties;
128  iterator = fwt->iterator;
129  if(ps){
130  index ++;
131  lenp = len_properties(ps);
132  if(index < lenp){
133  (*name) = ps[index].name;
134  (*jndex) = ps[index].index;
135  (*lastProp) = index;
136  (*type) = ps[index].type;
137  (*readOnly) = ps[index].readOnly;
138  return index;
139  }
140  }else if(iterator){
141  int iret = iterator(index, fwt, pointer, name, lastProp, jndex, type, readOnly);
142  if(iret > -1) return iret;
143  index++; //for functions below
144  }else{
145  index++; //may not have properties (or iterator) like SFFloat, which has a valueOf function
146  }
147  //next function
148  fs = fwt->Functions;
149  lenf = len_functions(fs);
150  ifindex = index - 1 - (*lastProp);
151  if(ifindex < lenf){
152  (*name) = fs[ifindex].name;
153  (*type) = 'f';
154  (*readOnly) = 'T';
155  return index;
156  }
157  return -1;
158 }
159 
160 int fwhas_generic(FWTYPE *fwt, void *pointer, const char *key, int *jndex, char *type, char *readOnly){
161  char *name;
162  int lastProp, isSet, index = -1;
163  lastProp = -1;
164  isSet = FALSE;
165 
166  while( (index = fwiterator_generic(index,fwt,pointer,&name, &lastProp, jndex, type, readOnly)) > -1){
167  if(!strcasecmp(name,key)){
168  //found it
169  return TRUE;
170  }
171  }
172  if(strlen(key)>4 && !strncmp(key,"set_",4))
173  isSet = TRUE;
174 
175  if(isSet){
176  char* key2 = &key[4];
177  while( (index = fwiterator_generic(index,fwt,pointer,&name, &lastProp, jndex, type, readOnly)) > -1){
178  if(!strcasecmp(name,key2)){
179  //found it
180  return TRUE;
181  }
182  }
183  }
184  return FALSE;
185 }
186 
187 
188 
189 typedef struct pJScript{
190  int ijunk;
191 }* ppJScript;
192 
193 
194 void *JScript_constructor(){
195  void *v = MALLOCV(sizeof(struct pJScript));
196  memset(v,0,sizeof(struct pJScript));
197  return v;
198 }
199 void JScript_init(struct tJScript *t){
200  //public
201  t->JSglobal_return_val = NULL;
202  //private
203  t->prv = JScript_constructor();
204  {
205  //ppJScript p = (ppJScript)t->prv;
206  //initialize statics
207  if(!FWTYPES_COUNT) initFWTYPEs();
208  }
209 }
210 // ppJScript p = (ppJScript)gglobal()->JScript.prv;
211 
212 //stubs the linker will be looking for
213 void jsVRMLBrowser_init(void *t){}
214 void jsUtils_init(void *t){}
215 void jsVRMLClasses_init(void *t){}
216 
217 
218 
219 
220 //==============ENGINE-AGNOSTIC HELPER CODE (could be extracted to other module) ====================
221 
222 
223 int isECMAtype(int itype){
224  int isEcma;
225  switch(itype){
226  case FIELDTYPE_SFBool:
227  case FIELDTYPE_SFFloat:
228  case FIELDTYPE_SFTime:
229  case FIELDTYPE_SFDouble:
230  case FIELDTYPE_SFInt32:
231  case FIELDTYPE_SFString:
232  isEcma = TRUE;
233  default:
234  isEcma = FALSE;
235  }
236  return isEcma;
237 }
238 
239 struct string_int{
240  char *c;
241  int i;
242 };
243 
244 struct string_int lookup_fieldType[] = {
245  {"Float", FIELDTYPE_SFFloat},
246  {"Rotation", FIELDTYPE_SFRotation},
247  {"Vec3f", FIELDTYPE_SFVec3f},
248  {"Bool", FIELDTYPE_SFBool},
249  {"Int32", FIELDTYPE_SFInt32},
250  {"Node", FIELDTYPE_SFNode},
251  {"Color", FIELDTYPE_SFColor},
252  {"ColorRGBA", FIELDTYPE_SFColorRGBA},
253  {"Time", FIELDTYPE_SFTime},
254  {"String", FIELDTYPE_SFString},
255  {"Vec2f", FIELDTYPE_SFVec2f},
256  {"Image", FIELDTYPE_SFImage},
257  {"Vec3d", FIELDTYPE_SFVec3d},
258  {"Double", FIELDTYPE_SFDouble},
259  {"Matrix3f", FIELDTYPE_SFMatrix3f},
260  {"Matrix3d", FIELDTYPE_SFMatrix3d},
261  {"Matrix4f", FIELDTYPE_SFMatrix4f},
262  {"Matrix4d", FIELDTYPE_SFMatrix4d},
263  {"Vec2d", FIELDTYPE_SFVec2d},
264  {"Vec4f", FIELDTYPE_SFVec4f},
265  {"Vec4d", FIELDTYPE_SFVec4d},
266  {NULL,0}
267 };
268 char * itype2string(int itype){
269  int i = 0;
270  while(lookup_fieldType[i].c){
271  if(lookup_fieldType[i].i == itype) return lookup_fieldType[i].c;
272  i++;
273  }
274  return NULL;
275 }
276 
277 int getFieldFromNodeAndName(struct X3D_Node* node,const char *fieldname, int *type, int *kind, int *iifield, union anyVrml **value);
278 
279 
280 int get_valueChanged_flag (int fptr, int actualscript){
281  char *fullname;
282  union anyVrml* value;
283  int type, kind, ifield, found;
284  struct X3D_Node *node;
285  struct Shader_Script *script;
286  struct ScriptFieldDecl *field;
287  struct CRscriptStruct *scriptcontrol; //, *ScriptControlArr = getScriptControl();
288  struct CRjsnameStruct *JSparamnames = getJSparamnames();
289 
290  scriptcontrol = getScriptControlIndex(actualscript); //&ScriptControlArr[actualscript];
291  script = scriptcontrol->script;
292  node = script->ShaderScriptNode;
293  fullname = JSparamnames[fptr].name;
294  found = getFieldFromNodeAndName(node,fullname,&type,&kind,&ifield,&value);
295  if(found){
296  field = Shader_Script_getScriptField(script, ifield);
297  gglobal()->JScript.JSglobal_return_val = (void *)&field->value;
298  return field->valueChanged;
299  }
300  gglobal()->JScript.JSglobal_return_val = NULL;
301  return 0;
302 }
303 void resetScriptTouchedFlag(int actualscript, int fptr){
304  char *fullname;
305  union anyVrml* value;
306  int type, kind, ifield, found;
307  struct X3D_Node *node;
308  struct Shader_Script *script;
309  struct ScriptFieldDecl *field;
310  struct CRscriptStruct *scriptcontrol; // *ScriptControlArr = getScriptControl();
311  struct CRjsnameStruct *JSparamnames = getJSparamnames();
312 
313  scriptcontrol = getScriptControlIndex(actualscript); //&ScriptControlArr[actualscript];
314  script = scriptcontrol->script;
315  node = script->ShaderScriptNode;
316  fullname = JSparamnames[fptr].name;
317  found = getFieldFromNodeAndName(node,fullname,&type,&kind,&ifield,&value);
318  if(found){
319  field = Shader_Script_getScriptField(script, ifield);
320  field->valueChanged = 0;
321  }
322  //printf("in get_valueChanged_flag\n");
323  return;
324 }
325 
326 //const char *stringFieldtypeType (int st); //in generatedcode
327 //const char *stringNodeType (int st);
328 int fwType2itype(const char *fwType){
329  int i, isSF, isMF, ifield = -1;
330  const char *suffix;
331  isSF = !strncmp(fwType,"SF",2);
332  isMF = !strncmp(fwType,"MF",2);
333  if(isSF || isMF){
334  suffix = &fwType[2]; //skip SF/MF part
335  i = 0;
336  while(lookup_fieldType[i].c){
337  if(!strcasecmp(suffix,lookup_fieldType[i].c)){
338  ifield = lookup_fieldType[i].i;
339  break;
340  }
341  i++;
342  }
343  if(ifield > -1 && isMF ) ifield++;
344  }else{
345  //browser and scene/executionContext shouldn't be going through fwconstructor
346  if(!strcasecmp(fwType,"Browser")) ifield = AUXTYPE_X3DBrowser;
347  if(!strcasecmp(fwType,"X3DConstants")) ifield = AUXTYPE_X3DConstants;
348  }
349  return ifield;
350 }
351 void freeField(int itype, void* any){
352  if(isSForMFType(itype) == 0){
353  //if(itype == FIELDTYPE_SFString){
354  // struct Uni_String *sf = (struct Uni_String*)any;
355  // if(sf) free(sf->strptr);
356  // free(sf);
357  //}
358  free(any); //SF
359  }else if(isSForMFType(itype) == 1){
360  //MF
361  struct Multi_Any* mf = (struct Multi_Any*)any;
362  //if(itype == FIELDTYPE_MFString){
363  // int i;
364  // struct Multi_String *ms = (struct Multi_String*)mf;
365  // for(i=0;i<ms->n;i++){
366  // struct Uni_String *sf = ms->p[i];
367  // if(sf) free(sf->strptr);
368  // free(sf);
369  // }
370  //}
371  free(mf->p); //if bombs, it could be because I'm not deep copying or medium_copy_field() everywhere I should
372  free(mf);
373  }
374 }
375 
376 #include <math.h> //for int = round(numeric)
377 unsigned long upper_power_of_two(unsigned long v);
378 void deleteMallocedFieldValue(int type,union anyVrml *fieldPtr);
379 void medium_copy_field0(int itype, void* source, void* dest)
380 {
381  /* medium-deep copies field up to and including pointer: doesn't deep copy *(SFNode*) or *(SFString*),
382  - SFString treated analogous to const char *
383  - malloc your starting type outside
384  */
385 
386  int i, sfsize,sformf;
387  int sftype, isMF;
388  struct Multi_Any *mfs,*mfd;
389 
390  sformf = isSForMFType(itype);
391  if(sformf < 0){
392  printf("bad type in medium_copy_field0\n");
393  return;
394  }
395  isMF = sformf == 1;
396  sftype = type2SF(itype);
397  //from EAI_C_CommonFunctions.c
398  sfsize = sizeofSF(sftype); //returnElementLength(sftype) * returnElementRowSize(sftype);
399  if(isMF)
400  {
401  int nele;
402  char *ps, *pd;
403  mfs = (struct Multi_Any*)source;
404  mfd = (struct Multi_Any*)dest;
405  //we need to malloc and do more copying
406  deleteMallocedFieldValue(itype,dest);
407  nele = mfs->n;
408  if( sftype == FIELDTYPE_SFNode ) nele = (int) upper_power_of_two(nele); //upper power of 2 is a convention for children[] to solve a realloc memory fragmentation issue during parsing of extremely large and flat files
409  mfd->p = malloc(sfsize*nele);
410  mfd->n = mfs->n;
411  ps = (char *)mfs->p;
412  pd = (char *)mfd->p;
413  for(i=0;i<mfs->n;i++)
414  {
415  medium_copy_field0(sftype,(union anyVrml*)ps,(union anyVrml*)pd);
416  ps += sfsize;
417  pd += sfsize;
418  }
419 
420  }else{
421  //isSF
422  memcpy(dest,source,sfsize);
423  }
424 } //return medium_copy_field
425 void medium_copy_field(int itype, void* source, void** dest){
426  //void *myDestination = NULL;
427  //medium_copy_field(itype,source,&myDestination);
428  // it will malloc the size
429  (*dest) = malloc(sizeofSForMF(itype));
430  memset((*dest),0,sizeofSForMF(itype));
431  medium_copy_field0(itype,source,(*dest));
432 }
433 
434 
435 static char *DefaultScriptMethodsA = "function initialize() {}; " \
436  " function shutdown() {}; " \
437  " function eventsProcessed() {}; " \
438  " TRUE=true; FALSE=false; " \
439  "";
440 
441 
442 static char *DefaultScriptMethodsB = " function print(x) {Browser.print(x)}; " \
443  " function println(x) {Browser.println(x)}; " \
444  " function getName() {return Browser.getName()}; "\
445  " function getVersion() {return Browser.getVersion()}; "\
446  " function getCurrentSpeed() {return Browser.getCurrentSpeed()}; "\
447  " function getCurrentFrameRate() {return Browser.getCurrentFrameRate()}; "\
448  " function getWorldURL() {return Browser.getWorldURL()}; "\
449  " function replaceWorld(x) {Browser.replaceWorld(x)}; "\
450  " function loadURL(x,y) {Browser.loadURL(x,y)}; "\
451  " function setDescription(x) {Browser.setDescription(x)}; "\
452  " function createVrmlFromString(x) {Browser.createVrmlFromString(x)}; "\
453  " function createVrmlFromURL(x,y,z) {Browser.createVrmlFromURL(x,y,z)}; "\
454  " function createX3DFromString(x) {Browser.createX3DFromString(x)}; "\
455  " function createX3DFromURL(x,y,z) {Browser.createX3DFromURL(x,y,z)}; "\
456  " function addRoute(a,b,c,d) {Browser.addRoute(a,b,c,d)}; "\
457  " function deleteRoute(a,b,c,d) {Browser.deleteRoute(a,b,c,d)}; "
458  "";
459 
460 /*add x3d v3.3 ecmascript X3DConstants table
461 // http://www.web3d.org/files/specifications/19777-1/V3.0/index.html
462 // http://www.web3d.org/files/specifications/19777-1/V3.0/Part1/functions.html
463 // 7.9.11
464 */
465 
466 
467 //==============START OF DUKTAPE-SPECIFIC CODE====================
468 #include "duktape/duktape.h"
469 
470 const char *duk_type_to_string(int duktype){
471  const char* r = NULL;
472  switch(duktype){
473  case DUK_TYPE_NUMBER: r = "DUK_TYPE_NUMBER"; break;
474  case DUK_TYPE_BOOLEAN: r = "DUK_TYPE_BOOLEAN"; break;
475  case DUK_TYPE_STRING: r = "DUK_TYPE_STRING"; break;
476  case DUK_TYPE_OBJECT: r = "DUK_TYPE_OBJECT"; break;
477  case DUK_TYPE_NONE: r = "DUK_TYPE_NONE"; break;
478  case DUK_TYPE_UNDEFINED: r = "DUK_TYPE_UNDEFINED"; break;
479  case DUK_TYPE_NULL: r = "DUK_TYPE_NULL"; break;
480  case DUK_TYPE_POINTER: r = "DUK_TYPE_POINTER"; break;
481  default:
482  r = "UNKNOWN_TYPE";
483  break;
484  }
485  return r;
486 }
487 
488 void show_stack(duk_context *ctx, char* comment)
489 {
490  int i, itop = duk_get_top(ctx);
491  if(comment) printf("%s top=%d\n",comment,itop);
492  //printf("%10s%10s%10s\n","position","type","more");
493  printf("%10s%10s\n","position","type");
494  for(i=0;i<itop;i++){
495  int ipos = -(i+1);
496  int t = duk_get_type(ctx, ipos);
497  char *stype = NULL;
498  const char * amore = "";
499  switch(t){
500  case DUK_TYPE_NUMBER: stype ="number"; break;
501  case DUK_TYPE_STRING: stype ="string"; break;
502 
503  case DUK_TYPE_OBJECT: stype ="object"; break;
504  case DUK_TYPE_NONE: stype ="none"; break;
505  case DUK_TYPE_UNDEFINED: stype ="undefined"; break;
506  case DUK_TYPE_BOOLEAN: stype ="boolean"; break;
507  case DUK_TYPE_NULL: stype ="null"; break;
508  case DUK_TYPE_POINTER: stype ="pointer"; break;
509  default:
510  stype = "unknown";
511  }
512  if(duk_is_function(ctx,ipos)){
513  char *afunc = "";
514  afunc = duk_is_c_function(ctx,ipos) ? "Cfunc" : afunc;
515  afunc = duk_is_ecmascript_function(ctx,ipos) ? "jsfunc" : afunc;
516  afunc = duk_is_bound_function(ctx,ipos) ? "boundfunc" : afunc;
517  amore = afunc;
518  }
519  if(duk_is_nan(ctx,ipos)){
520  amore = "NaN";
521  }
522  if(duk_is_object(ctx,ipos)){
523 
524  }
525  printf("%10d%10s %s\n",ipos,stype,amore);
526  }
527 }
528 
529 //Object virtualization via proxy objects: constructor, handlers (has,ownKeys,enumerate,get,set,deleteProp), finalizer
530 
531 // >> PROXY CACHING FUNCTIONS
532 // 2017 - lazy proxies have been too lazy, we created a new "Proxy" on ever fetch
533 // x and that meant if(ground == ground) would always be false if ground is a proxy
534 // x in js there's no proxy trap (function overload) just for the binary == scenario,
535 // x and no way to override binary == operator
536 // - can do if(ground.valueOf() == ground.valueOf()) and over-ride valueOf (working now Jan 2017),
537 // x but that's unconventional syntax
538 // - duktape creator Sami says try caching your proxies
539 // - then (ground == ground) still won't be comparing x3d node addresses,
540 // but will return true because the proxy instances will be the same
541 // - that means per-context/ctx caching
542 // - and since we don't have a way to hook into javascript scope push and pop
543 // we need to rely on finalizer for a place to remove a proxy from our cache / lookup table
544 // Jan 11, 2017 proxy caching is working, now ground==ground and scenarios like
545 // val[1] == ground are true if they are supposed to be the same node
546 // still some wasteful re-generation of proxies
547 // but within the scope of the == operator, its working, which is better than before caching
548 // UNFINISHED BUISNESS JAN 2017: the lookup table for finding the cache based on ctx
549 // could be changed so Script->_cache to save one lookup loop
550 static Stack * proxycaches = NULL;
551 typedef struct cache_table_entry {
552  duk_context *ctx;
553  Stack *cache;
554 } cache_entry;
555 typedef struct proxy_cache_entry {
556  struct X3D_Node *node;
557  // native proxy
558  // js proxy
559  void *jsproxy;
560 } proxy_entry;
561 
562 cache_entry * lookup_ctx_proxy_cache(duk_context *ctx){
563  int i;
564  cache_entry *ret = NULL;
565  if(proxycaches == NULL){
566  proxycaches = newStack(cache_entry *); //* so can NULL if/when script node deleted, without needing to pack
567  }
568  for(i=0;i<vectorSize(proxycaches);i++){
569  cache_entry * ce = vector_get(cache_entry*,proxycaches,i);
570  if(ce->ctx == ctx){
571  ret = ce;
572  break;
573  }
574  }
575  if(ret == NULL){
576  cache_entry *ce = MALLOC(cache_entry*,sizeof(cache_entry));
577  stack_push(cache_entry*,proxycaches,ce);
578  ce->ctx = ctx;
579  ce->cache = newStack(proxy_entry*); //* so can NULL in cfinalizer without needing to pack table
580  ret = ce;
581  }
582  return ret;
583 }
584 proxy_entry *lookup_ctx_proxycache_entry_by_nodeptr(duk_context *ctx, struct X3D_Node *node){
585  proxy_entry *ret = NULL;
586  cache_entry* cache = lookup_ctx_proxy_cache(ctx);
587  if(cache){
588  int i;
589  for(i=0;i<vectorSize(cache->cache);i++){
590  proxy_entry *pe = vector_get(proxy_entry*,cache->cache,i);
591  if(pe && pe->node == node){
592  ret = pe;
593  }
594  }
595  }
596  return ret;
597 }
598 proxy_entry *add_ctx_proxycache_entry(duk_context *ctx, struct X3D_Node *node, void *jsproxy){
599  int i;
600  //assume we already verified it doesn't exist
601  proxy_entry *ret = NULL;
602  cache_entry *cache = lookup_ctx_proxy_cache(ctx);
603  if(cache){
604  int i, itarget;
605  proxy_entry *pe = MALLOC(proxy_entry*,sizeof(proxy_entry));
606  pe->node = node;
607  pe->jsproxy = jsproxy;
608 
609  itarget = -1;
610  for(i=0;i<vectorSize(cache->cache);i++){
611  proxy_entry *pe0 = vector_get(proxy_entry*,cache->cache,i);
612  if(pe0 == NULL){
613  itarget = i;
614  vector_set(proxy_entry*,cache->cache,i,pe);
615  ret = pe;
616  break;
617  }
618  }
619  if(itarget == -1){
620  stack_push(proxy_entry*,cache->cache,pe);
621  ret = pe;
622  }
623  if(0){
624  printf("cache after add proxy\n");
625  for(i=0;i<vectorSize(cache->cache);i++){
626  proxy_entry *pe0 = vector_get(proxy_entry*,cache->cache,i);
627  if(pe0)
628  printf("%d %x %x\n",i,pe0->node,pe0->jsproxy);
629  else
630  printf("%d NULL\n",i);
631  }
632  }
633  }
634  return ret;
635 }
636 void remove_ctx_proxycache_entry_by_nodeptr(duk_context *ctx, struct X3D_Node *node){
637  int i;
638  //Q. is it dangerous / should we always remove by jsproxy* ?
639  proxy_entry *ret = NULL;
640  cache_entry *cache = lookup_ctx_proxy_cache(ctx);
641  if(cache){
642  int i;
643  for(i=0;i<vectorSize(cache->cache);i++){
644  proxy_entry *pe0 = vector_get(proxy_entry*,cache->cache,i);
645  if(pe0 && pe0->node == node){
646  vector_set(proxy_entry*,cache->cache,i,NULL);
647  FREE_IF_NZ(pe0);
648  break;
649  }
650  }
651  if(0){
652  printf("after cache clean\n");
653  for(i=0;i<vectorSize(cache->cache);i++){
654  proxy_entry *pe0 = vector_get(proxy_entry*,cache->cache,i);
655  if(pe0)
656  printf("%d %x %x\n",i,pe0->node,pe0->jsproxy);
657  else
658  printf("%d NULL\n",i);
659  }
660  }
661  }
662 }
663 void remove_ctx_proxycache_entry_by_jsproxy(duk_context *ctx, void *jsproxy){
664  int i;
665  proxy_entry *ret = NULL;
666  cache_entry *cache = lookup_ctx_proxy_cache(ctx);
667  if(cache){
668  int i;
669  for(i=0;i<vectorSize(cache->cache);i++){
670  proxy_entry *pe0 = vector_get(proxy_entry*,cache->cache,i);
671  if(pe0 && pe0->jsproxy == jsproxy){
672  vector_set(proxy_entry*,cache->cache,i,NULL);
673  FREE_IF_NZ(pe0);
674  break;
675  }
676  }
677  }
678 }
679 //<< PROXY CACHING FUNCTIONS
680 
681 int cfinalizer(duk_context *ctx){
682  int rc, itype, igc;
683  void *fwpointer = NULL;
684  itype = igc = -1;
685  rc = duk_get_prop_string(ctx,0,"fwItype");
686  if(rc == 1) itype = duk_to_int(ctx,-1);
687  duk_pop(ctx); //get prop string result
688  rc = duk_get_prop_string(ctx,0,"fwGC");
689  if(rc == 1) igc = duk_to_boolean(ctx,-1);
690  duk_pop(ctx); //get prop string result
691  rc = duk_get_prop_string(ctx,0,"fwField");
692  if(rc == 1) fwpointer = duk_to_pointer(ctx,-1);
693  duk_pop(ctx); //get prop string result
694 
695 
696  //printf("hi from finalizer, itype=%s igc=%d p=%p\n",itype2string(itype),igc,fwpointer);
697  if(itype == FIELDTYPE_SFNode && fwpointer){
698  //2017 remove proxy from context cache
699  //
700  //a) lookup context cache
701  //b) lookup node's proxy in context cache
702  //c) remove
703  struct X3D_Node *node = *(struct X3D_Node**)fwpointer;
704  remove_ctx_proxycache_entry_by_nodeptr(ctx, node);
705  }
706  if(igc > 0 && itype > -1 && fwpointer){
707  if(itype < AUXTYPE_X3DConstants){
708  //FIELDS
709  freeField(itype,fwpointer);
710  }else{
711  //AUXTYPES
712  free(fwpointer);
713  }
714  }
715  return 0;
716 }
717 
718 static int doingFinalizer = 1;
719 void push_typed_proxy(duk_context *ctx, int itype, void *fwpointer, int* valueChanged)
720 {
721  //like push_typed_proxy2 except push this instead of push obj
722  //int rc;
723  proxy_entry *pe = NULL;
724  if(itype == FIELDTYPE_SFNode){
725  struct X3D_Node* node = *(struct X3D_Node**)fwpointer;
726  //printf("pushtyped nodetype %d\n",node->_nodeType);
727  pe = lookup_ctx_proxycache_entry_by_nodeptr(ctx, node);
728  }
729  if(pe){
730  duk_push_heapptr(ctx, pe->jsproxy);
731  }else{
732  //show_stack(ctx,"push_typed_proxy start");
733  duk_eval_string(ctx,"Proxy");
734  duk_push_this(ctx); //this
735  duk_push_pointer(ctx,fwpointer);
736  duk_put_prop_string(ctx,-2,"fwField");
737  duk_push_pointer(ctx,valueChanged);
738  duk_put_prop_string(ctx,-2,"fwChanged");
739  duk_push_int(ctx,itype);
740  duk_put_prop_string(ctx,-2,"fwItype");
741  if(doingFinalizer){
742  duk_push_boolean(ctx,TRUE);
743  duk_put_prop_string(ctx,-2,"fwGC");
744  }
745  duk_eval_string(ctx,"handler");
746  //show_stack(ctx,"push_typed_proxy should have Proxy, this, handler");
747 
748  duk_new(ctx,2); /* [ global Proxy target handler ] -> [ global result ] */
749  //show_stack(ctx,"push_typed_proxy after new, proxy obj should be result???");
750 
751  if(doingFinalizer){
752  //push_typed_proxy is called by constructor, that mallocs (via fwtype->constructor) and should GC
753  //
754  //Duktape.fin(a, function (x) {
755  // try {
756  // print('finalizer, foo ->', x.foo);
757  // } catch (e) {
758  // print('WARNING: finalizer failed (ignoring): ' + e);
759  // }
760  // });
761  if(itype == FIELDTYPE_SFNode){
762  struct X3D_Node* node = *(struct X3D_Node**)fwpointer;
763  void *jsproxy = duk_get_heapptr(ctx, -1);
764  add_ctx_proxycache_entry(ctx, node, jsproxy);
765  }
766  duk_eval_string(ctx,"Duktape.fin");
767  duk_dup(ctx, -2); //copy the proxy object
768  duk_push_c_function(ctx,cfinalizer,1);
769  duk_pcall(ctx,2);
770  duk_pop(ctx); //pop Duktape.fin result
771  }
772  }
773 }
774 
775 int push_typed_proxy2(duk_context *ctx, int itype, int kind, void *fwpointer, int* valueChanged, char doGC)
776 {
777  /* like fwgetter version, except with no fieldname or mode, for temp proxies
778  nativePtr
779  */
780  //int rc;
781  proxy_entry *pe = NULL;
782  int idogc = doGC ? TRUE : FALSE;
783  if(itype == FIELDTYPE_SFNode){
784  struct X3D_Node* node = *(struct X3D_Node**)fwpointer;
785  //printf("pushtyped2 nodetype %d\n",node->_nodeType);
786  pe = lookup_ctx_proxycache_entry_by_nodeptr(ctx, node);
787  }
788  if(pe){
789  duk_push_heapptr(ctx, pe->jsproxy);
790  }else{
791  duk_eval_string(ctx,"Proxy");
792  duk_push_object(ctx);
793  duk_push_pointer(ctx,fwpointer);
794  duk_put_prop_string(ctx,-2,"fwField");
795  duk_push_pointer(ctx,valueChanged);
796  duk_put_prop_string(ctx,-2,"fwChanged");
797  duk_push_int(ctx,itype);
798  duk_put_prop_string(ctx,-2,"fwItype");
799  duk_push_int(ctx,kind);
800  duk_put_prop_string(ctx,-2,"fwKind");
801 
802  if(doingFinalizer) { // && idogc){
803  duk_push_boolean(ctx,idogc);
804  duk_put_prop_string(ctx,-2,"fwGC");
805  }
806 
807  duk_eval_string(ctx,"handler");
808  duk_new(ctx,2); /* [ global Proxy target handler ] -> [ global result ] */
809  if(doingFinalizer) { // && idogc){
810  //push_typed_proxy2 _refers_ to script->field[i]->anyVrml (its caller fwgetter doesn't malloc) and should not GC its pointer
811  //
812  //Duktape.fin(a, function (x) {
813  // try {
814  // print('finalizer, foo ->', x.foo);
815  // } catch (e) {
816  // print('WARNING: finalizer failed (ignoring): ' + e);
817  // }
818  // });
819  if(itype == FIELDTYPE_SFNode){
820  struct X3D_Node* node = *(struct X3D_Node**)fwpointer;
821  void *jsproxy = duk_get_heapptr(ctx, -1);
822  add_ctx_proxycache_entry(ctx, node, jsproxy);
823  }
824 
825  duk_eval_string(ctx,"Duktape.fin");
826  duk_dup(ctx, -2); //copy the proxy object
827  duk_push_c_function(ctx,cfinalizer,1);
828  duk_pcall(ctx,2);
829  duk_pop(ctx); //pop Duktape.fin result
830  }
831  }
832 
833  return 1;
834 }
835 
836 
837 
838 void convert_duk_to_fwvals(duk_context *ctx, int nargs, int istack, struct ArgListType arglist, FWval *args, int *argc){
839  int nUsable,nNeeded, i, ii;
840  FWval pars;
841  //struct Uni_String *uni;
842  nUsable = arglist.iVarArgStartsAt > -1 ? nargs : arglist.nfixedArg;
843  nNeeded = max(nUsable,arglist.nfixedArg);
844  pars = malloc(nNeeded*sizeof(FWVAL));
845  (*args) = pars;
846  //QC and genericization of incoming parameters
847  (*argc) = nNeeded;
848  for(i=0;i<nUsable;i++){
849  //const char* str;
850  int trhs; //RHS or incoming javascript primitive type
851  char ctype; //LHS or target type
852  ii = istack + i;
853  if(i < arglist.nfixedArg)
854  ctype = arglist.argtypes[i];
855  else
856  ctype = arglist.argtypes[arglist.iVarArgStartsAt];
857  pars[i].itype = ctype;
858  if( duk_is_object(ctx, ii)){
859  int rc, isPrimitive;
860  //if the script goes myField = new String('hi'); then it comes in here as an object (versus myField = 'hi'; which is a string)
861  rc = duk_get_prop_string(ctx,ii,"fwItype");
862  duk_pop(ctx);
863  isPrimitive = rc == 0;
864  if(isPrimitive){
865  //void duk_to_primitive(duk_context *ctx, duk_idx_t index, duk_int_t hint); DUK_HINT_NONE
866  //http://www.duktape.org/api.html#duk_to_primitive
867  duk_to_primitive(ctx,ii,DUK_HINT_NONE);
868  }
869  }
870  //determine RHS / actual ecma type on stack
871  trhs = duk_get_type(ctx, ii);
872  //switch(trhs){
873  // case DUK_TYPE_NUMBER: stype ="number"; break;
874  // case DUK_TYPE_STRING: stype ="string"; break;
875 
876  // case DUK_TYPE_OBJECT: stype ="object"; break;
877  // case DUK_TYPE_NONE: stype ="none"; break;
878  // case DUK_TYPE_UNDEFINED: stype ="undefined"; break;
879  // case DUK_TYPE_BOOLEAN: stype ="boolean"; break;
880  // case DUK_TYPE_NULL: stype ="null"; break;
881  // case DUK_TYPE_POINTER: stype ="pointer"; break;
882  // default:
883  //}
884  //if( duk_is_null(ctx,ii)){
885  // printf("rhs is null\n");
886  //}
887 
888  switch(ctype){
889  case 'B': {
890  int bb = duk_get_boolean(ctx,ii); //duk_to_boolean(ctx,ii);
891  pars[i]._boolean = bb; // duk_to_boolean(ctx,ii);
892  }
893  break;
894  case 'I': pars[i]._integer = duk_to_int(ctx,ii); break;
895  case 'F': pars[i]._numeric = duk_to_number(ctx,ii); break;
896  case 'D': pars[i]._numeric = duk_to_number(ctx,ii); break;
897  case 'S': pars[i]._string = duk_to_string(ctx,ii); break;
898  case 'Z': //flexi-string idea - allow either String or MFString (no such thing as SFString from ecma - it uses String for that)
899  if(duk_is_string(ctx,ii)){
900  pars[i]._string = duk_get_string(ctx,ii);
901  pars[i].itype = 'S';
902  break;
903  }
904  if(!duk_is_object(ctx,i))
905  break;
906  //else fall through to W
907  case 'W': {
908  int rc, isOK, itypeRHS = -1;
909  union anyVrml *fieldRHS = NULL;
910  if(trhs == DUK_TYPE_NULL){
911  itypeRHS = 10;
912  fieldRHS = malloc(sizeof(union anyVrml));
913  fieldRHS->sfnode = NULL;
914  }else if(trhs == DUK_TYPE_OBJECT){
915  rc = duk_get_prop_string(ctx,ii,"fwItype");
916  if(rc == 1){
917  itypeRHS = duk_to_int(ctx,-1);
918  }
919  duk_pop(ctx);
920  rc = duk_get_prop_string(ctx,ii,"fwField");
921  if(rc == 1) fieldRHS = duk_to_pointer(ctx,-1);
922  duk_pop(ctx);
923  }
924  /*we don't need the RHS fwChanged=valueChanged* because we are only changing the LHS*/
925  isOK = FALSE;
926  //if(fieldRHS != NULL && itypeRHS > -1){
927  if(itypeRHS > -1){
928  // its one of our proxy field types or null. But is it the type we need?
929  //medium_copy_field(itypeRHS,fieldRHS,&pars[i]._web3dval.native); //medium copy - copies p[] in MF types but not deep copy *(p[i]) if p[i] is pointer type ie SFNode* or Uni_String*
930  pars[i]._web3dval.native = fieldRHS;
931  pars[i]._web3dval.fieldType = itypeRHS;
932  pars[i].itype = 'W';
933  // see below *valueChanged = TRUE;
934  isOK = TRUE;
935  }
936  }
937  break;
938  case 'P': {
939  int rc, isOK, itypeRHS = -1;
940  union anyVrml *fieldRHS = NULL;
941  rc = duk_get_prop_string(ctx,ii,"fwItype");
942  if(rc == 1){
943  //printf(duk_type_to_string(duk_get_type(ctx, -1)));
944  itypeRHS = duk_to_int(ctx,-1);
945  }
946  duk_pop(ctx);
947  rc = duk_get_prop_string(ctx,ii,"fwField");
948  if(rc == 1) fieldRHS = duk_to_pointer(ctx,-1);
949  duk_pop(ctx);
950  /*we don't need the RHS fwChanged=valueChanged* because we are only changing the LHS*/
951  isOK = FALSE;
952  if(fieldRHS != NULL && itypeRHS >= AUXTYPE_X3DConstants){
953  /* its one of our auxiliary types - Browser, X3DConstants, ProfileInfo, ComponentInfo, X3DRoute ...*/
954  pars[i]._pointer.native = fieldRHS;
955  pars[i]._pointer.fieldType = itypeRHS;
956  pars[i].itype = 'P';
957  // see below *valueChanged = TRUE;
958  isOK = TRUE;
959  }
960  }
961  break;
962 
963  case 'O': break; //object pointer ie to js function callback object
964  }
965  }
966 
967  for(i=nUsable;i<nNeeded;i++){
968  //fill
969  char ctype = arglist.argtypes[i];
970  pars[i].itype = ctype;
971  switch(ctype){
972  case 'B': pars[i]._boolean = FALSE; break;
973  case 'I': pars[i]._integer = 0; break;
974  case 'F': pars[i]._numeric = 0.0; break;
975  case 'D': pars[i]._numeric = 0.0; break;
976  case 'S': pars[i]._string = NULL; break;
977  case 'Z': pars[i]._string = NULL; pars[i].itype = 'S'; break;
978  case 'W':
979  pars[i]._web3dval.fieldType = FIELDTYPE_SFNode;
980  pars[i]._web3dval.native = NULL; break;
981  //case 'P':
982  // pars[i]._web3dval.fieldType = FIELDTYPE_SFNode; //I don't have a good default value - do I need an AUXTYPE_NULL?
983  // pars[i]._web3dval.native = NULL; break;
984  case 'O':
985  pars[i]._jsobject = NULL; break;
986  default:
987  pars[i].itype = '0';
988  }
989  }
990 }
991 
992 
993 int cfwconstructor(duk_context *ctx) {
994  int i, j, rc, nargs, argc, ifound;
995  FWTYPE *fwt;
996  FWval args;
997  void *fwpointer;
998  int *valueChanged = NULL; //so called 'internal' variables inside the script context don't point to a valueChanged
999  int itype = -1;
1000  nargs = duk_get_top(ctx);
1001 
1002  //show_stack(ctx,"cfwconstructor start");
1003 
1004  duk_push_current_function(ctx);
1005  rc = duk_get_prop_string(ctx,-1,"fwItype");
1006  if(rc == 1) itype = duk_to_int(ctx,-1);
1007  duk_pop(ctx); //get prop string result
1008  duk_pop(ctx); //current function
1009 
1010  //show_stack(ctx,"cfwconstructor after push and pop current function");
1011 
1012  if(itype < 0) return 0; //no itype means it's not one of ours
1013  fwt = getFWTYPE(itype);
1014  if(!fwt->Constructor) return 0;
1015 
1016  //find the contructor that matches the args best
1017  i = 0;
1018  ifound = -1;
1019  while(fwt->ConstructorArgs[i].nfixedArg > -1){
1020  int nfixed = fwt->ConstructorArgs[i].nfixedArg;
1021  int ivarsa = fwt->ConstructorArgs[i].iVarArgStartsAt;
1022  char *neededTypes = fwt->ConstructorArgs[i].argtypes;
1023  int fill = fwt->ConstructorArgs[i].fillMissingFixedWithZero == 'T';
1024  if( nargs == nfixed || (ivarsa > -1 && nargs >= nfixed ) || (ivarsa > -1 && fill)){
1025  //nargs is a match
1026  int allOK = TRUE;
1027  //check each narg for compatible type
1028  for(j=0;j<nargs;j++){
1029  char neededType;
1030  int isOK, RHS_duk_type = duk_get_type(ctx, j);
1031  isOK = FALSE;
1032  neededType = j >= nfixed ? neededTypes[ivarsa] : neededTypes[j]; //if you have varargs you specify one more type than the fixed requires
1033  // for example MFColor nfixed=0 (you can have 0 to infinity args), ivarsa=0 (varargs start at index 0), neededTypes="W" the first and subsequent varargs are of type 'W'
1034  //printf("duktype %s\n",duk_type_to_string(RHS_duk_type));
1035  switch(RHS_duk_type){
1036  case DUK_TYPE_NUMBER:
1037  if(neededType =='F' || neededType =='D' || neededType =='I') isOK = TRUE;
1038  break;
1039  case DUK_TYPE_BOOLEAN:
1040  if(neededType =='B') isOK = TRUE;
1041  break;
1042  case DUK_TYPE_STRING:
1043  if(neededType =='S' || neededType =='Z') isOK = TRUE;
1044  break;
1045  case DUK_TYPE_OBJECT:
1046  if(neededType =='W' || neededType =='P'){
1047  int rc, itypeRHS = -1;
1048  union anyVrml *fieldRHS = NULL;
1049  rc = duk_get_prop_string(ctx,j,"fwItype");
1050  if(rc == 1){
1051  //printf(duk_type_to_string(duk_get_type(ctx, -1)));
1052  itypeRHS = duk_to_int(ctx,-1);
1053  }
1054  duk_pop(ctx);
1055  rc = duk_get_prop_string(ctx,j,"fwField");
1056  if(rc == 1) fieldRHS = duk_to_pointer(ctx,-1);
1057  duk_pop(ctx);
1058  //we don't need the RHS fwChanged=valueChanged* because we are only changing the LHS
1059 
1060  if(fieldRHS != NULL && itypeRHS > -1){
1061  //in theory, we could make sure somehow that we had the right kind of 'W' : add a FIELDTYPE_ / AUXTYPE_ array in arglist struct
1062  isOK = TRUE;
1063  }
1064  }
1065  break;
1066  case DUK_TYPE_NONE:
1067  case DUK_TYPE_UNDEFINED:
1068  case DUK_TYPE_NULL:
1069  // are we attempting to null out the field? we aren't allowed to change its type (to undefined)
1070  case DUK_TYPE_POINTER:
1071  // don't know what this would be for if anything
1072  default:
1073  isOK = FALSE;
1074  break;
1075  }
1076  allOK = allOK && isOK;
1077  }
1078  if(fill)
1079  for(j=nargs;j<nfixed;j++){
1080  allOK = allOK && 1;
1081  }
1082  if(allOK){
1083  ifound = i;
1084  break;
1085  }
1086  }
1087  i++;
1088  }
1089  if(ifound < 0){
1090  //printf("matching constructor not found, you have %d args for %s\n",nargs,fwt->name);
1091  //Jan 2016 if you're in here, and your Script did new MFString(number,string) -heterogenous call args-
1092  //.. then I think the problem is the nested loops above are inside-out. MFString constructor
1093  //.. should be able to handle heterogenous args, and if constructor.args was the inner loop and
1094  //.. call args the outer loop, allOK would be true:
1095  //.. it would find the (only) constructor is a match.
1096  //.. don't have time to code-review, try and test this theory thoroughly today
1097  //.. temproary fix in your script: new MFString(number.toString(),string) to make args homogenous.
1098  //return 0;
1099  i = 0; //take the first one
1100  }
1101  args = NULL;
1102  convert_duk_to_fwvals(ctx, nargs, 0, fwt->ConstructorArgs[i], &args, &argc);
1103  if(fwt->ConstructorArgs[ifound].fillMissingFixedWithZero == 'T' && nargs < fwt->ConstructorArgs[ifound].nfixedArg){
1104  int nfixed = fwt->ConstructorArgs[ifound].nfixedArg;
1105  //int ivarsa = fwt->ConstructorArgs[ifound].iVarArgStartsAt;
1106  char *neededTypes = fwt->ConstructorArgs[ifound].argtypes;
1107  //int fill = fwt->ConstructorArgs[ifound].fillMissingFixedWithZero == 'T';
1108  args = realloc(args,nfixed * sizeof(FWVAL));
1109  for(j=nargs;j<nfixed;j++){
1110  switch(neededTypes[j]){
1111  case 'B':
1112  args[j]._boolean = FALSE; break;
1113  case 'I':
1114  args[j]._integer = 0; break;
1115  case 'F':
1116  args[j]._numeric = 0.0; break;
1117  case 'D':
1118  args[j]._numeric = 0.0; break;
1119  case 'S':
1120  args[j]._string = ""; break;
1121  case 'W':
1122  case 'P':
1123  break;
1124  }
1125  }
1126  argc = nfixed;
1127  }
1128 
1129  fwpointer = fwt->Constructor(fwt,argc,args);
1130  free(args);
1131  push_typed_proxy(ctx,itype, fwpointer, valueChanged);
1132 
1133  return 1;
1134 }
1135 int chas(duk_context *ctx) {
1136  int rc, itype, *valueChanged;
1137  const char *key;
1138  int nr, index;
1139  char type, readOnly;
1140  FWTYPE *fwt;
1141  union anyVrml *parent = NULL;
1142 
1143  itype = 0;
1144  /* get type of parent object for this property*/
1145  rc = duk_get_prop_string(ctx,0,"fwItype");
1146  if(rc==1) itype = duk_get_int(ctx,-1);
1147  duk_pop(ctx);
1148  /* get the pointer to the parent object */
1149  rc = duk_get_prop_string(ctx,0,"fwField");
1150  if(rc == 1) parent = duk_to_pointer(ctx,-1);
1151  duk_pop(ctx);
1152  /* get the pointer to the changed flag */
1153  rc = duk_get_prop_string(ctx,0,"fwChanged");
1154  if(rc == 1) valueChanged = duk_to_pointer(ctx,-1);
1155  duk_pop(ctx);
1156  key = duk_require_string(ctx,-1);
1157  //printf("key=%s\n",key);
1158 
1159  nr = 1;
1160  fwt = getFWTYPE(itype);
1161  if(fwhas_generic(fwt,parent,key,&index,&type,&readOnly)){
1162  duk_push_true(ctx);
1163  }else{
1164  duk_push_false(ctx);
1165  }
1166  //isFunc = type == 'f';
1167  //show_stack(ctx,"in chas");
1168 
1169  return nr;
1170 }
1171 int cownKeys(duk_context *ctx) {
1172  int rc, itype, *valueChanged, arr_idx;
1173  void *parent = NULL;
1174  int i;
1175  char *fieldname;
1176  int lastProp, jndex; //isFunc,
1177  char type, readOnly;
1178  //FWTYPE *getFWTYPE(int itype)
1179  FWTYPE *fwt;
1180  itype = -1;
1181 
1182  /* get type of parent object for this property*/
1183  rc = duk_get_prop_string(ctx,0,"fwItype");
1184  if(rc==1) itype = duk_get_int(ctx,-1);
1185  duk_pop(ctx);
1186  /* get the pointer to the parent object */
1187  rc = duk_get_prop_string(ctx,0,"fwField");
1188  if(rc == 1) parent = duk_to_pointer(ctx,-1);
1189  duk_pop(ctx);
1190  /* get the pointer to the changed flag */
1191  rc = duk_get_prop_string(ctx,0,"fwChanged");
1192  if(rc == 1) valueChanged = duk_to_pointer(ctx,-1);
1193  duk_pop(ctx);
1194 
1195  arr_idx = duk_push_array(ctx);
1196  if(itype < 0 || (itype < AUXTYPE_X3DConstants && parent == NULL))
1197  return 1; //return empty array
1198  i = -1;
1199  fwt = getFWTYPE(itype);
1200  //fwiterator_generic(int index, FWTYPE *fwt, FWPointer *pointer, char **name, int *lastProp, int *jndex)
1201  while( (i = fwiterator_generic(i,fwt,parent,&fieldname,&lastProp,&jndex,&type,&readOnly)) > -1 ){
1202  duk_push_string(ctx, fieldname);
1203  duk_put_prop_index(ctx, arr_idx, i);
1204  }
1205  //show_stack(ctx,"in cownKeys");
1206  return 1;
1207 }
1208 int cenumerate(duk_context *ctx) {
1209  int rc, itype, *valueChanged;
1210  union anyVrml *parent = NULL;
1211  int i;
1212  char *fieldname;
1213  int lastProp, jndex; //isFunc,
1214  char type, readOnly;
1215  FWTYPE *fwt;
1216  int arr_idx;
1217 
1218  itype =0;
1219  /* get type of parent object for this property*/
1220  rc = duk_get_prop_string(ctx,0,"fwItype");
1221  if(rc==1) itype = duk_get_int(ctx,-1);
1222  duk_pop(ctx);
1223  /* get the pointer to the parent object */
1224  rc = duk_get_prop_string(ctx,0,"fwField");
1225  if(rc == 1) parent = duk_to_pointer(ctx,-1);
1226  duk_pop(ctx);
1227  /* get the pointer to the changed flag */
1228  rc = duk_get_prop_string(ctx,0,"fwChanged");
1229  if(rc == 1) valueChanged = duk_to_pointer(ctx,-1);
1230  duk_pop(ctx);
1231 
1232  arr_idx = duk_push_array(ctx);
1233  i = -1;
1234  fwt = getFWTYPE(itype);
1235  //fwiterator_generic(int index, FWTYPE *fwt, FWPointer *pointer, char **name, int *lastProp, int *jndex)
1236  while( (i = fwiterator_generic(i,fwt,parent,&fieldname,&lastProp,&jndex,&type,&readOnly)) > -1 ){
1237  //isFunc = i > lastProp;
1238  duk_push_string(ctx, fieldname);
1239  duk_put_prop_index(ctx, arr_idx, i);
1240  }
1241  //show_stack(ctx,"in cenumerate");
1242  return 1;
1243 }
1244 
1245 int push_duk_fieldvalueECMA(duk_context *ctx, int itype, union anyVrml *fieldvalue)
1246 {
1247  /*we have the field, and even the key name.
1248  So we should be able to decide how to package the outgoing value type:
1249  according to specs:
1250  - return ecma primitive value type for SFBool, SFInt32, SFFloat, SFDouble, SFTime, SFString
1251  - return our field-type-specific object/proxy-wrapper, pointing to our global.field, for the others.
1252  */
1253  int nr;
1254  //int isOK = FALSE;
1255  nr = 1;
1256  switch(itype){
1257  case FIELDTYPE_SFBool:
1258  duk_push_boolean(ctx,fieldvalue->sfbool); break;
1259  case FIELDTYPE_SFFloat:
1260  duk_push_number(ctx,fieldvalue->sffloat); break;
1261  case FIELDTYPE_SFTime:
1262  duk_push_number(ctx,fieldvalue->sftime); break;
1263  case FIELDTYPE_SFDouble:
1264  duk_push_number(ctx,fieldvalue->sfdouble); break;
1265  case FIELDTYPE_SFInt32:
1266  duk_push_int(ctx,fieldvalue->sfint32); break;
1267  case FIELDTYPE_SFString:
1268  duk_push_string(ctx,fieldvalue->sfstring->strptr); break;
1269  default:
1270  nr = 0;
1271  break;
1272  }
1273  //show_stack(ctx,"in fwgetterNS at end");
1274  return nr;
1275 }
1276 
1277 static int SCALARS_ARE_PRIMITIVES = TRUE;
1278 /* SCALARS_ARE_PRIMITIVES
1279  the ecmascript ! operator invokes ToBoolean() which always returns true when the argument is an object
1280  http://www.ecma-international.org/ecma-262/5.1/#sec-11.4.9
1281  http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
1282  the web3d.org ecmascript specs say all fields shall have getType(), isReadable(), isWritable() functions.
1283  if I have: Script {
1284  field SFBool enabled TRUE
1285  url "ecmascript: function initialize(){
1286  var A = !enabled; //A returns false if enabled is a primitive and its value is true,
1287  //but A always returns false if enabled is a proxy object
1288  var B = enabled.getType(); //eval fails with 'type error, not an object' if enabled is a primitive,
1289  //but B returns X3DConstants.SFBool if enabled is a proxy object
1290  Because there are some goodies either way, and I'm not sure what the specs intend, I've made it configurable for now,
1291  although comparisons with vivaty are closer to SCALARS_ARE_PRIMITIVES = TRUE (some scenes fail with FALSE).
1292 */
1293 int fwval_duk_push(duk_context *ctx, FWval fwretval, int *valueChanged){
1294  //converts engine-agnostic FWVAL return value to duk engine specific return values and pushes them onto the duk value stack
1295  int nr = 1;
1296  switch(fwretval->itype){
1297 
1298  case 'B':
1299  duk_push_boolean(ctx,fwretval->_boolean); break;
1300  case 'I':
1301  duk_push_int(ctx,fwretval->_integer); break;
1302  case 'F':
1303  duk_push_number(ctx,fwretval->_numeric); break;
1304  case 'D':
1305  duk_push_number(ctx,fwretval->_numeric); break;
1306  case 'S':
1307  duk_push_string(ctx,fwretval->_string); break;
1308 
1309  case 'W':
1310  if(SCALARS_ARE_PRIMITIVES){
1311  //for pointers to web3d field types
1312  switch(fwretval->_web3dval.fieldType){
1313  case FIELDTYPE_SFBool:
1314  duk_push_boolean(ctx,fwretval->_web3dval.anyvrml->sfbool); break;
1315  case FIELDTYPE_SFInt32:
1316  duk_push_int(ctx,fwretval->_web3dval.anyvrml->sfint32); break;
1317  case FIELDTYPE_SFFloat:
1318  duk_push_number(ctx,(double)fwretval->_web3dval.anyvrml->sffloat); break;
1319  case FIELDTYPE_SFDouble:
1320  case FIELDTYPE_SFTime:
1321  duk_push_number(ctx,fwretval->_web3dval.anyvrml->sfdouble); break;
1322  case FIELDTYPE_SFString:
1323  duk_push_string(ctx,fwretval->_web3dval.anyvrml->sfstring->strptr); break;
1324  default:
1325  push_typed_proxy2(ctx,fwretval->_web3dval.fieldType,fwretval->_web3dval.kind,fwretval->_web3dval.native,valueChanged,fwretval->_web3dval.gc);
1326  }
1327  }else{
1328  //SCALARS_ARE_PROXY_OBJECTS
1329  push_typed_proxy2(ctx,fwretval->_web3dval.fieldType,fwretval->_web3dval.kind,fwretval->_web3dval.native,valueChanged,fwretval->_web3dval.gc);
1330  }
1331  break;
1332  case 'X':
1333  duk_push_pointer(ctx,fwretval->_jsobject);
1334  break;
1335  case 'P':
1336  //for web3d auxiliary types Browser, X3DFieldDefinitionArray, X3DRoute ...
1337  push_typed_proxy2(ctx,fwretval->_pointer.fieldType,fwretval->_pointer.kind,fwretval->_pointer.native,valueChanged,fwretval->_pointer.gc);
1338  break;
1339  case '0':
1340  default:
1341  nr = 0; break;
1342  }
1343  return nr;
1344 }
1345 
1346 int ctypefunction(duk_context *ctx) {
1347  int rc, nr, itype, kind, nargs;
1348  const char *fwFunc = NULL;
1349  //union anyVrml* field = NULL;
1350  //FWTYPE *fwt;
1351 
1352  itype = -1;
1353  kind = -1;
1354  nargs = duk_get_top(ctx);
1355  //show_stack(ctx,"in cfuction");
1356  duk_push_current_function(ctx);
1357  /* get type of parent object for this property*/
1358  rc = duk_get_prop_string(ctx,-1,"fwItype");
1359  if(rc==1) itype = duk_get_int(ctx,-1);
1360  duk_pop(ctx);
1361  /*get the PKW_inputOutput read/write mode for the parent field*/
1362  rc = duk_get_prop_string(ctx,-1,"fwKind");
1363  if(rc==1) kind = duk_get_int(ctx,-1);
1364  duk_pop(ctx);
1365  /* get the name of the function called */
1366  rc = duk_get_prop_string(ctx,-1,"fwFunc");
1367  if(rc == 1) fwFunc = duk_to_string(ctx,-1);
1368  duk_pop(ctx);
1369  duk_pop(ctx); //durrent function
1370  nr = 0;
1371  if(!strcasecmp(fwFunc,"getType")){
1372  duk_push_int(ctx,itype);
1373  nr = 1;
1374  }
1375  if(!strcmp(fwFunc,"isReadable")){
1376  int isreadable = TRUE;
1377  if(kind > -1)
1378  isreadable = isreadable && (kind == PKW_inputOutput || kind == PKW_initializeOnly);
1379  if(isreadable) duk_push_true(ctx);
1380  else duk_push_false(ctx);
1381  nr = 1;
1382  }
1383  if(!strcmp(fwFunc,"isWritable")){
1384  int iswritable = TRUE;
1385  if(kind > -1)
1386  iswritable = iswritable && (kind == PKW_inputOutput || kind == PKW_outputOnly);
1387  if(iswritable) duk_push_true(ctx);
1388  else duk_push_false(ctx);
1389  nr = 1;
1390  }
1391  return nr;
1392 }
1393 int cfunction(duk_context *ctx) {
1394  int rc, nr, itype, nargs, *valueChanged = NULL;
1395  const char *fwFunc = NULL;
1396  union anyVrml* parent = NULL;
1397  //union anyVrml* field = NULL;
1398  FWTYPE *fwt;
1399  FWFunctionSpec *fs;
1400 
1401  itype = 0;
1402  nargs = duk_get_top(ctx);
1403  //show_stack(ctx,"in cfuction");
1404  duk_push_current_function(ctx);
1405  /* get type of parent object for this property*/
1406  rc = duk_get_prop_string(ctx,-1,"fwItype");
1407  if(rc==1) itype = duk_get_int(ctx,-1);
1408  duk_pop(ctx);
1409  /* get the pointer to the parent object */
1410  rc = duk_get_prop_string(ctx,-1,"fwField");
1411  if(rc == 1) parent = duk_to_pointer(ctx,-1);
1412  duk_pop(ctx);
1413  /* get the pointer to the changed flag */
1414  rc = duk_get_prop_string(ctx,-1,"fwChanged");
1415  if(rc == 1) valueChanged = duk_to_pointer(ctx,-1);
1416  duk_pop(ctx);
1417  /* get the name of the function called */
1418  rc = duk_get_prop_string(ctx,-1,"fwFunc");
1419  if(rc == 1) fwFunc = duk_to_string(ctx,-1);
1420  duk_pop(ctx);
1421  duk_pop(ctx); //durrent function
1422 
1423  nr = 0;
1424 
1425  fwt = getFWTYPE(itype);
1426  //check functions - if its a function push the type's specfic function
1427  fs = getFWFunc(fwt,fwFunc);
1428  if(fs){
1429  FWval pars;
1430  int argc;
1431  FWVAL fwretval;
1432  struct X3D_Node *scriptnode;
1433  void *ec = NULL;
1434  convert_duk_to_fwvals(ctx, nargs, 0, fs->arglist, &pars, &argc);
1435  //the object function call, using engine-agnostic parameters
1436 
1437  //>>just SFNode function getNodeName needs to know the script node context (it can't use its own - it may be an IMPORT)
1438  duk_eval_string(ctx,"__script");
1439  scriptnode = (struct X3D_Node*) duk_to_pointer(ctx,-1);
1440  duk_pop(ctx);
1441  if(scriptnode)
1442  ec = (void *)scriptnode->_executionContext;
1443  //<<
1444  nr = fs->call(fwt,ec,parent,argc,pars,&fwretval);
1445  if(nr){
1446  nr = fwval_duk_push(ctx,&fwretval,valueChanged);
1447  if(nr && !strcasecmp(fwFunc,"toString")){
1448  if(fwretval.itype == 'S' && fwretval._string){
1449  //printf("gcing toString string %s\n",fwretval._string);
1450  free(fwretval._string); //if this bombs take it out and toString strings won't be gcd. There's nothing set up to gc _string in general
1451  }
1452  }
1453  }else{
1454  if(valueChanged) *valueChanged = TRUE;
1455  }
1456  free(pars);
1457  }
1458  return nr;
1459 }
1460 int cget(duk_context *ctx) {
1461  int rc, nr, itype, kind, *valueChanged = NULL;
1462  //show_stack(ctx,"in cget");
1463  union anyVrml* parent = NULL;
1464  //union anyVrml* field = NULL;
1465 
1466  /* get type of parent object for this property*/
1467  itype = -1;
1468  kind = -1;
1469  rc = duk_get_prop_string(ctx,0,"fwItype");
1470  if(rc==1) itype = duk_get_int(ctx,-1);
1471  duk_pop(ctx);
1472  /* get the kind of parent field PKW_inputOutput etc*/
1473  rc = duk_get_prop_string(ctx,0,"fwKind");
1474  if(rc==1) kind = duk_get_int(ctx,-1);
1475  duk_pop(ctx);
1476  /* get the pointer to the parent object */
1477  rc = duk_get_prop_string(ctx,0,"fwField");
1478  if(rc == 1) parent = duk_to_pointer(ctx,-1);
1479  duk_pop(ctx);
1480  /* get the pointer to the changed flag */
1481  rc = duk_get_prop_string(ctx,0,"fwChanged");
1482  if(rc == 1) valueChanged = duk_to_pointer(ctx,-1);
1483  duk_pop(ctx);
1484  //show_stack(ctx,"in cget");
1485 
1486  nr = 0;
1487  //printf("indexer is%s\n",duk_type_to_string(duk_get_type(ctx,-2)));
1488  switch(duk_get_type(ctx,-2)){
1489  case DUK_TYPE_NUMBER:{
1490  //int ikey = duk_get_int(ctx,-2);
1491  //printf("key=[%d]",ikey);
1492  }
1493  break;
1494  default: {
1495  const char *key = duk_require_string(ctx,-2);
1496  //printf("key=%s \n",key);
1497  if(!strcmp(key,"fwItype")){
1498  //someone else is asking a proxy for its fwItype (for example LHS = RHSProxy) the LHS Setter may want the RHS's fwItype
1499  duk_push_int(ctx,itype);
1500  nr = 1;
1501  return nr;
1502  }
1503  if(!strcmp(key,"fwGC")){
1504  //someone else is asking a proxy for its fwGC (for example LHS = RHSProxy)
1505  //if there's no fwGC already on it, then the answer is FALSE
1506  duk_push_boolean(ctx,FALSE);
1507  nr = 1;
1508  return nr;
1509  }
1510  if(!strcmp(key,"fwField")){
1511  //someone is asking a proxy for its fwField
1512  duk_push_pointer(ctx,parent);
1513  nr = 1;
1514  return nr;
1515  }
1516  if(!strcasecmp(key,"getType") || !strcmp(key,"isReadable") || !strcmp(key,"isWritable")){
1517  //its a function all auxtypes and fieldtypes share
1518  duk_push_c_function(ctx,ctypefunction,DUK_VARARGS);
1519  duk_push_int(ctx,itype);
1520  duk_put_prop_string(ctx,-2,"fwItype");
1521  duk_push_int(ctx,kind);
1522  duk_put_prop_string(ctx,-2,"fwKind");
1523  duk_push_string(ctx,key);
1524  duk_put_prop_string(ctx,-2,"fwFunc");
1525  nr = 1;
1526  return nr;
1527  }
1528  }
1529  break;
1530  }
1531 
1532 
1533  if(itype > -1){
1534  //itype is in AUXTYPE_ range
1535  const char *key = NULL;// = duk_require_string(ctx,-2);
1536  FWTYPE *fwt = getFWTYPE(itype);
1537  int jndex, found;
1538  char type, readOnly;
1539  found = 0;
1540 
1541  //check numeric indexer
1542  if(duk_is_number(ctx,-2)){
1543  //indexer
1544  int index = duk_get_int(ctx,-2);
1545  if(fwt->takesIndexer){
1546  type = fwt->takesIndexer;
1547  readOnly = fwt->indexerReadOnly;
1548  jndex = index;
1549  found = 1;
1550  }else{
1551  //script is attempting to iterate over/get properties by number to get value - good luck
1552  const char *name;
1553  int lastProp;
1554  index = fwiterator_generic(index -1,fwt,parent,&name,&lastProp,&jndex,&type,&readOnly);
1555  if(index > -1) found = 1;
1556  }
1557  }else{
1558  //check properties - if a property, call the type-specific setter
1559  //int lastProp;
1560  key = duk_get_string(ctx,-2);
1561  found = fwhas_generic(fwt,parent,key,&jndex,&type,&readOnly);
1562  if(!found && strcmp(key,"valueOf")){
1563  //annoying valueOf ususally thunks properly to toString, so just show other keys not found
1564  static int once = 0;
1565  if(!once)
1566  ConsoleMessage("type %s has no property or function %s - please check your typing\n",fwt->name,key);
1567  once = 1;
1568  }
1569  }
1570  if(found && type=='f'){
1571  FWFunctionSpec *fw = getFWFunc(fwt,key);
1572  if(fw){
1573  //its a function
1574  duk_push_c_function(ctx,cfunction,DUK_VARARGS);
1575  duk_push_pointer(ctx,parent);
1576  duk_put_prop_string(ctx,-2,"fwField");
1577  duk_push_pointer(ctx,valueChanged);
1578  duk_put_prop_string(ctx,-2,"fwChanged");
1579  duk_push_int(ctx,itype);
1580  duk_put_prop_string(ctx,-2,"fwItype");
1581  duk_push_string(ctx,key);
1582  duk_put_prop_string(ctx,-2,"fwFunc");
1583  nr = 1;
1584  }
1585  }else if(found && fwt->Getter){
1586  FWVAL fwretval;
1587  struct X3D_Node *scriptnode;
1588  void *ec = NULL;
1589  //>>just SFNode function getNodeName needs to know the script node context (it can't use its own - it may be an IMPORT)
1590  duk_eval_string(ctx,"__script");
1591  scriptnode = (struct X3D_Node*) duk_to_pointer(ctx,-1);
1592  duk_pop(ctx);
1593  if(scriptnode)
1594  ec = (void *)scriptnode->_executionContext;
1595  //<<
1596 
1597  nr = fwt->Getter(fwt,jndex,ec,parent,&fwretval);
1598  if(nr){
1599  nr = fwval_duk_push(ctx,&fwretval,valueChanged);
1600  }
1601  }
1602  }
1603  return nr;
1604 }
1605 int cset(duk_context *ctx) {
1606  int rc, itype, *valueChanged = NULL;
1607  union anyVrml *parent = NULL;
1608  itype = -1;
1609  /* get type of parent object for this property*/
1610  rc = duk_get_prop_string(ctx,0,"fwItype");
1611  if(rc==1) itype = duk_get_int(ctx,-1);
1612  duk_pop(ctx);
1613  /* get the pointer to the parent object */
1614  rc = duk_get_prop_string(ctx,0,"fwField");
1615  if(rc == 1) parent = duk_to_pointer(ctx,-1);
1616  duk_pop(ctx);
1617  /* get the pointer to the changed flag */
1618  rc = duk_get_prop_string(ctx,0,"fwChanged");
1619  if(rc == 1) valueChanged = duk_to_pointer(ctx,-1);
1620  duk_pop(ctx);
1621 
1622 
1623  switch(duk_get_type(ctx,-3)){
1624  case DUK_TYPE_NUMBER:{
1625  //int ikey = duk_get_int(ctx,-3);
1626  //printf("key=[%d] ",ikey);
1627  }
1628  break;
1629  default: {
1630  //const char *key = duk_require_string(ctx,-3);
1631  //printf("key=%s ",key);
1632  }
1633  break;
1634  }
1635  switch(duk_get_type(ctx,-2)){
1636  case DUK_TYPE_NUMBER:{
1637  int ival = duk_get_int(ctx,-2);
1638  //printf("val=[%d]\n",ival);
1639  }
1640  break;
1641  case DUK_TYPE_STRING:{
1642  const char *cval = duk_get_string(ctx,-2);
1643  //printf("val=%s\n",cval);
1644  }
1645  break;
1646  default:
1647  //printf("val is object\n");
1648  break;
1649  }
1650 
1651 
1652  if(itype > -1) {
1653  //itype is in FIELDTYPE_ and AUXTYPE_ range
1654  const char* key;
1655  FWTYPE *fwt = getFWTYPE(itype);
1656  int jndex, found;
1657  char type, readOnly;
1658  //check numeric indexer
1659  if(duk_is_number(ctx,-3) && fwt->takesIndexer){
1660  //indexer
1661  jndex = duk_get_int(ctx,-3);
1662  type = fwt->takesIndexer;
1663  readOnly = fwt->indexerReadOnly;
1664  found = 1;
1665  }else{
1666  //check properties - if a property, call the type-specific setter
1667  //int lastProp;
1668  key = duk_get_string(ctx,-3);
1669  found = fwhas_generic(fwt,parent,key,&jndex,&type,&readOnly) && (type != 'f');
1670  }
1671  if(found && (readOnly != 'T') && fwt->Setter){
1672  FWval fwsetval = NULL;
1673  struct ArgListType arglist;
1674  int argc;
1675  arglist.argtypes = &type;
1676  arglist.fillMissingFixedWithZero = 0;
1677  arglist.nfixedArg = 1;
1678  arglist.iVarArgStartsAt = -1;
1679  convert_duk_to_fwvals(ctx, 1, -2, arglist, &fwsetval, &argc);
1680  if(argc == 1){
1681  struct X3D_Node *scriptnode;
1682  void *ec = NULL;
1683  //>>just SFNode function getNodeName needs to know the script node context (it can't use its own - it may be an IMPORT)
1684  duk_eval_string(ctx,"__script");
1685  scriptnode = (struct X3D_Node*) duk_to_pointer(ctx,-1);
1686  duk_pop(ctx);
1687  if(scriptnode)
1688  ec = (void *)scriptnode->_executionContext;
1689  //<<
1690 
1691  fwt->Setter(fwt,jndex,ec,parent,fwsetval);
1692  if(valueChanged)
1693  (*valueChanged) = 1;
1694  }
1695  free(fwsetval);
1696  }
1697  }
1698  return 0;
1699 }
1700 int cdel(duk_context *ctx) {
1701  int rc, itype, *valueChanged;
1702  union anyVrml *parent;
1703 
1704  /* get type of parent object for this property*/
1705  rc = duk_get_prop_string(ctx,0,"fwItype");
1706  if(rc==1) itype = duk_get_int(ctx,-1);
1707  duk_pop(ctx);
1708  //if(fwType) printf("fwType in cget=%s\n",fwType);
1709  /* get the pointer to the parent object */
1710  rc = duk_get_prop_string(ctx,0,"fwField");
1711  if(rc == 1) parent = duk_to_pointer(ctx,-1);
1712  duk_pop(ctx);
1713  /* get the pointer to the changed flag */
1714  rc = duk_get_prop_string(ctx,0,"fwChanged");
1715  if(rc == 1) valueChanged = duk_to_pointer(ctx,-1);
1716  duk_pop(ctx);
1717 
1718  show_stack(ctx,"in cdel");
1719  //duk_push_string(ctx, nativeValue);
1720  return 1;
1721 }
1722 
1723 //c-side helper adds the generic handler to global, for use when creating each proxy
1724 void addHandler(duk_context *ctx){
1725  int iglobal, ihandler; // , rc;
1726  iglobal = duk_get_top(ctx) -1;
1727 
1728  duk_push_object(ctx);
1729  duk_put_prop_string(ctx, iglobal, "handler");
1730 
1731  duk_get_prop_string(ctx,iglobal,"handler"); //get handler from global
1732  ihandler = duk_get_top(ctx) -1; //+ve
1733  duk_push_c_function(ctx,chas,2);
1734  duk_put_prop_string(ctx, ihandler, "has");
1735  duk_push_c_function(ctx,cownKeys,1);
1736  duk_put_prop_string(ctx, ihandler, "ownKeys");
1737  duk_push_c_function(ctx,cenumerate,1);
1738  duk_put_prop_string(ctx, ihandler, "enumerate");
1739  duk_push_c_function(ctx,cget,3);
1740  duk_put_prop_string(ctx, ihandler, "get");
1741  duk_push_c_function(ctx,cset,4);
1742  duk_put_prop_string(ctx, ihandler, "set");
1743  duk_push_c_function(ctx,cdel,2);
1744  duk_put_prop_string(ctx, ihandler, "del");
1745  duk_pop(ctx); //pop handler off stack
1746 
1747 }
1748 void addCustomProxyType(duk_context *ctx, int iglobal, const char *typeName)
1749 {
1750  int itype;
1751  duk_push_c_function(ctx,cfwconstructor,DUK_VARARGS);
1752  //put fname=SFVec3f on c_function, so in constructor we can tell what we are trying to construct
1753  itype = fwType2itype(typeName);
1754  duk_push_int(ctx,itype);
1755  duk_put_prop_string(ctx,-2,"fwItype");
1756  //put SFVec3f = c_fuction on global
1757  duk_put_prop_string(ctx,iglobal,typeName);
1758 }
1759 void add_duk_global_property(duk_context *ctx, int itype, const char *fieldname, int *valueChanged, struct X3D_Node *node);
1760 
1761 
1762 /* www.duktape.org javascript engine used here */
1763 //A DUK helper function
1764 static char *eval_string_defineAccessor = "\
1765 function defineAccessor(obj, key, set, get) { \
1766  Object.defineProperty(obj, key, { \
1767  enumerable: true, configurable: true, \
1768  set: set, get: get \
1769  }); \
1770 }";
1771 
1772 
1773 
1774 /* create the script context for this script. This is called from the thread
1775  that handles script calling in the fwl_RenderSceneUpdateScene */
1776 void JSCreateScriptContext(int num) {
1777  int i, iglobal; // , rc;
1778  //jsval rval;
1779  duk_context *ctx; /* these are set here */
1780  struct Shader_Script *script;
1781  struct X3D_Node *scriptnode;
1782  //JSObject *_globalObj; /* these are set here */
1783  //BrowserNative *br; /* these are set here */
1784  //ppJScript p = (ppJScript)gglobal()->JScript.prv;
1785  struct CRscriptStruct *ScriptControl; // = getScriptControl();
1786 
1787  ScriptControl = getScriptControlIndex(num);
1788  script = ScriptControl->script;
1789  scriptnode = script->ShaderScriptNode;
1790  //CREATE CONTEXT
1791  ctx = duk_create_heap_default();
1792 
1793  //ADD STANDARD JS GLOBAL OBJECT/CLASSES
1794  duk_push_global_object(ctx);
1795  iglobal = duk_get_top(ctx) -1;
1796 
1797  //SAVE OUR CONTEXT IN OUR PROGRAM'S SCRIPT NODE FOR LATER RE-USE
1798  ScriptControl->cx = ctx;
1799  //ScriptControl[num].glob = (void *)malloc(sizeof(int));
1800  //*((int *)ScriptControl[num].glob) = iglobal; //we'll be careful not to pop our global for this context (till context cleanup)
1801  ((int *)&ScriptControl->glob)[0] = iglobal; //we'll be careful not to pop our global for this context (till context cleanup)
1802 
1803  //ADD HELPER PROPS AND FUNCTIONS
1804  duk_push_pointer(ctx,scriptnode); //I don't think we need to know the script this way, but in the future, you might
1805  duk_put_prop_string(ctx,iglobal,"__script"); //oct 2014 the future arrived. sfnode.getNodeName needs the DEFnames from the broto context, and script seems to know its broto context
1806 
1807  duk_push_string(ctx,eval_string_defineAccessor);
1808  duk_eval(ctx);
1809  duk_pop(ctx);
1810 
1811  //ADD CUSTOM TYPES - Browser, X3DConstants, web3d field types
1812  addHandler(ctx); //add helper called handler, to global object
1813  //add types that can be newed ie var a = new SFVec3f();
1814  // they will have a non-null constructor function
1815  // generally, it's all our SF and MF field types
1816  for(i=0;i<FWTYPES_COUNT;i++)
1817  if(fwtypesArray[i]->Constructor)
1818  addCustomProxyType(ctx,iglobal,fwtypesArray[i]->name);
1819  //show_stack(ctx,"before adding Browser");
1820  //add static singltons on global object ie global.Browser global.X3DConstants
1821  add_duk_global_property(ctx, AUXTYPE_X3DBrowser, "Browser", NULL, NULL);
1822  add_duk_global_property(ctx, AUXTYPE_X3DConstants,"X3DConstants", NULL, NULL);
1823  //add Global methods and defines for VMRL/X3D (some redirecting to the Browser object ie print = Browser.println)
1824  duk_eval_string(ctx,DefaultScriptMethodsA);
1825  duk_pop(ctx);
1826  duk_eval_string(ctx,DefaultScriptMethodsB);
1827  duk_pop(ctx);
1828 
1829  /* send this data over to the routing table functions. */
1830  CRoutes_js_new (num, JAVASCRIPT);
1831 
1832  //tests, if something is broken these tests might help
1833  if(1){
1834  void *scriptnode;
1835  struct X3D_Node *snode;
1836  //duk_eval_string(ctx,"print('this.__script='+this.__script);"); //checks the NodeScript availability
1837  //duk_pop(ctx);
1838  duk_eval_string(ctx,"__script");
1839  scriptnode = duk_to_pointer(ctx,-1);
1840  duk_pop(ctx);
1841  snode = (struct X3D_Node *)scriptnode;
1842  //printf("script node = %p",scriptnode);
1843  }
1844  if(0){
1845  duk_eval_string(ctx,"print(Object.keys(Browser));"); //invokes ownKeys
1846  duk_pop(ctx);
1847  duk_eval_string(ctx,"print(Object.getOwnPropertyNames(Browser));"); //invokes ownKeys
1848  duk_pop(ctx);
1849  duk_eval_string(ctx,"for (k in Browser) {print(k);}"); //invokes enumerate
1850  duk_pop(ctx);
1851  duk_eval_string(ctx,"if('println' in Browser) print('have println'); else print('no println');"); //invokes has
1852  duk_pop(ctx);
1853  duk_eval_string(ctx,"print('X3DConstants.outputOnly='); print(X3DConstants.outputOnly);"); //invokes custom iterator in generic has
1854  duk_pop(ctx);
1855  duk_eval_string(ctx,"print(Object.keys(X3DConstants));"); //invokes custom iterator in ownKeys
1856  duk_pop(ctx);
1857  }
1858  if(0){
1859  duk_eval_string(ctx,"Browser.println('hi from brwsr.println');");
1860  duk_pop(ctx);
1861  duk_eval_string(ctx,"Browser.description = 'funny description happened on the way to ..';");
1862  duk_pop(ctx);
1863  duk_eval_string(ctx,"Browser.println(Browser.description);");
1864  duk_pop(ctx);
1865  duk_eval_string(ctx,"print('hi from print');");
1866  duk_pop(ctx);
1867  duk_eval_string(ctx,"print(Browser.version);");
1868  duk_pop(ctx);
1869 
1870  }
1871  if(0){
1872  duk_eval_string(ctx,"print('Browser.supportedComponents.length = ');");duk_pop(ctx);
1873  duk_eval_string(ctx,"print(Browser.supportedComponents.length);"); duk_pop(ctx);
1874  duk_eval_string(ctx,"for(var i=0;i<Browser.supportedComponents.length;i++) {print(Browser.supportedComponents[i].name + ' '+Browser.supportedComponents[i].level);}"); duk_pop(ctx);
1875  }
1876  if(0){
1877  duk_eval_string(ctx,"var myvec3 = new SFVec3f(1.0,2.0,3.0);");
1878  duk_pop(ctx);
1879  duk_eval_string(ctx,"print(myvec3.x.toString());");
1880  duk_pop(ctx);
1881  duk_eval_string(ctx,"myvec3.y = 45.0;");
1882  duk_pop(ctx);
1883  duk_eval_string(ctx,"print('sb45='+myvec3.y);");
1884  duk_pop(ctx);
1885  }
1886 
1887  return;
1888 }
1889 
1890 
1891 
1892 /* fwsetterNS, fwgetterNS are for our Script node dynamic fields, or
1893  more precisely, for the javascript property we create on the js>context>global object,
1894  one global>property for each script field, with the name of the script field on the property
1895 */
1896 
1897 int SFNode_Setter0(FWType fwt, int index, void *ec, void * fwn, FWval fwval, int isCurrentScriptNode);
1898 int fwsetterNS(duk_context *ctx) {
1899  /* myfield = new SFVec3f(1,2,3);
1900  * if myfield is a property we set on the global object, and we've assigned this setter to it,
1901  * we'll come in here. We can set the *valueChanged and value on the LHS object, by copying, as per specs
1902  * terminology: LHS: left hand side of equation (ie myfield) RHS: right hand side of equation (ie result of new SFVec3f() )
1903  * if we come in here for AUXTYPES we should not write - AUXTYPE_X3DBrowser, and AUXTYPE_X3DConstants are static singletons
1904  */
1905  int nargs; // , nr;
1906  int rc, itype, *valueChanged;
1907  //union anyVrml *field;
1908  const char *key;
1909  struct X3D_Node* parent = NULL;
1910  nargs = duk_get_top(ctx);
1911 
1912  /* retrieve key from nonstandard arg */
1913  //show_stack(ctx,"in fwsetterNS");
1914  // nativeValue = duk_require_string(ctx, 0);
1915  //implicit key by setter C function //char *key = duk_require_string(ctx, 1);
1916  key = duk_require_string(ctx,1); //"myprop";
1917  //printf("\nfwsetterNS, key=%s value=%s\n",key,nativeValue);
1918 
1919  itype = -1;
1920  /* get details of LHS object */
1921  /* retrieve field pointer from Cfunc */
1922  duk_push_current_function(ctx);
1923  /* get type of parent object for this property*/
1924  rc = duk_get_prop_string(ctx,-1,"fwItype");
1925  if(rc==1) itype = duk_get_int(ctx,-1);
1926  duk_pop(ctx);
1927  if(itype > -1 && itype < AUXTYPE_X3DConstants){
1928  //our script fields
1929  rc = duk_get_prop_string(ctx,-1,"fwNode");
1930  if(rc==1) parent = duk_to_pointer(ctx,-1);
1931  duk_pop(ctx);
1932  /* get the pointer to the changed flag */
1933  rc = duk_get_prop_string(ctx,-1,"fwChanged");
1934  if(rc == 1) valueChanged = duk_to_pointer(ctx,-1);
1935  duk_pop(ctx);
1936  }
1937  duk_pop(ctx); //pop current function
1938 
1939  if(itype > -1 && itype < AUXTYPE_X3DConstants){
1940  //code borrowed from cget and modified to not set setEventIn on self (auto-eventing this script)
1941  //const char* key;
1942  FWTYPE *fwt = getFWTYPE(FIELDTYPE_SFNode);
1943  int jndex, found;
1944  char type, readOnly;
1945  //check properties - if a property, call the type-specific setter
1946  //int lastProp;
1947  union anyVrml any;
1948  any.sfnode = parent;
1949 
1950  found = fwhas_generic(fwt,&any,key,&jndex,&type,&readOnly) && (type != 'f');
1951  if(found){
1952  FWval fwsetval = NULL;
1953  struct ArgListType arglist;
1954  int argc;
1955  arglist.argtypes = &type;
1956  arglist.fillMissingFixedWithZero = 0;
1957  arglist.nfixedArg = 1;
1958  arglist.iVarArgStartsAt = -1;
1959  convert_duk_to_fwvals(ctx, 1, -2, arglist, &fwsetval, &argc);
1960  if(argc == 1){
1961  struct X3D_Node *scriptnode;
1962  void *ec = NULL;
1963  //>>just SFNode function getNodeName needs to know the script node context (it can't use its own - it may be an IMPORT)
1964  duk_eval_string(ctx,"__script");
1965  scriptnode = (struct X3D_Node*) duk_to_pointer(ctx,-1);
1966  duk_pop(ctx);
1967  if(scriptnode)
1968  ec = (void *)scriptnode->_executionContext;
1969  //<<
1970 
1971  SFNode_Setter0(fwt,jndex,ec,&any,fwsetval,TRUE);
1972  //if(valueChanged)
1973  // (*valueChanged) = 1; //DONE IN SFNODE_SETTER0
1974  }
1975  free(fwsetval);
1976  }
1977  }
1978  return 0;
1979 }
1980 
1981 void push_typed_proxy_fwgetter(duk_context *ctx, int itype, int mode, const char* fieldname, void *fwpointer, int* valueChanged)
1982 {
1983  /* called by fwgetter (for referenced script->fields)
1984  1. push_object (fresh object)
1985  2. fwpointer: reference to script->field[i]->anyvrml
1986  */
1987  //int rc;
1988  proxy_entry *pe = NULL;
1989  if(itype == FIELDTYPE_SFNode){
1990  struct X3D_Node* node = *(struct X3D_Node**)fwpointer;
1991  printf("pushtyped2 nodetype %d\n",node->_nodeType);
1992  pe = lookup_ctx_proxycache_entry_by_nodeptr(ctx, node);
1993  }
1994  if(pe){
1995  duk_push_heapptr(ctx,pe->jsproxy);
1996  }else{
1997 
1998  duk_eval_string(ctx,"Proxy");
1999  duk_push_object(ctx);
2000  duk_push_pointer(ctx,fwpointer);
2001  duk_put_prop_string(ctx,-2,"fwField");
2002  duk_push_pointer(ctx,valueChanged);
2003  duk_put_prop_string(ctx,-2,"fwChanged");
2004  duk_push_int(ctx,itype);
2005  duk_put_prop_string(ctx,-2,"fwItype");
2006  duk_eval_string(ctx,"handler");
2007  duk_new(ctx,2); /* [ global Proxy target handler ] -> [ global result ] */
2008 
2009  //2017 >
2010  if(doingFinalizer) { // && idogc){
2011  //push_typed_proxy2 _refers_ to script->field[i]->anyVrml (its caller fwgetter doesn't malloc) and should not GC its pointer
2012  //
2013  //Duktape.fin(a, function (x) {
2014  // try {
2015  // print('finalizer, foo ->', x.foo);
2016  // } catch (e) {
2017  // print('WARNING: finalizer failed (ignoring): ' + e);
2018  // }
2019  // });
2020  if(itype == FIELDTYPE_SFNode){
2021  struct X3D_Node* node = *(struct X3D_Node**)fwpointer;
2022  void *jsproxy = duk_get_heapptr(ctx, -1);
2023  add_ctx_proxycache_entry(ctx, node, jsproxy);
2024  }
2025  duk_eval_string(ctx,"Duktape.fin");
2026  duk_dup(ctx, -2); //copy the proxy object
2027  duk_push_c_function(ctx,cfinalizer,1);
2028  duk_pcall(ctx,2);
2029  duk_pop(ctx); //pop Duktape.fin result
2030  }
2031  }
2032 
2033 }
2034 
2035 
2036 int push_duk_fieldvalue(duk_context *ctx, int itype, int mode, const char* fieldname, union anyVrml *field, int *valueChanged)
2037 {
2038  /*we have the field, and even the key name.
2039  So we should be able to decide how to package the outgoing value type:
2040  according to specs:
2041  - return ecma primitive value type for SFBool, SFInt32, SFFloat, SFDouble, SFTime, SFString
2042  - return our field-type-specific object/proxy-wrapper, pointing to our global.field, for the others.
2043  */
2044  int nr;
2045  nr = 0;
2046  if(field){
2047  //int isOK = FALSE;
2048  nr = 1;
2049  switch(itype){
2050  case FIELDTYPE_SFBool:
2051  duk_push_boolean(ctx,field->sfbool); break;
2052  case FIELDTYPE_SFFloat:
2053  duk_push_number(ctx,field->sffloat); break;
2054  case FIELDTYPE_SFTime:
2055  duk_push_number(ctx,field->sftime); break;
2056  case FIELDTYPE_SFDouble:
2057  duk_push_number(ctx,field->sfdouble); break;
2058  case FIELDTYPE_SFInt32:
2059  duk_push_int(ctx,field->sfint32); break;
2060  case FIELDTYPE_SFString:
2061  duk_push_string(ctx,field->sfstring->strptr); break;
2062  default:
2063  //we need an object with our c handlers and pointer to our script->field[i]
2064  if(0){
2065  if(itype == FIELDTYPE_SFNode){
2066  //test to compare anyVrml.SFNode with X3D_Node*
2067  //typedef struct X3D_Node* vrmlNodeT;
2068 
2069  struct X3D_Node *anode;
2070  //anode = (struct X3D_Node *)(field); //WRONG (but how? H0: struct/union word alignment WRONG H1: off by a pointer * or & RIGHT)
2071  //anode = (struct X3D_Node *)&(field); //WRONG
2072  //anode = (struct X3D_Node *)&(*field); //WRONG
2073  //anode = (struct X3D_Node *)(*field); //I think this is numerically RIGHT, but syntactically awkward/WRONG for compilers
2074  (memcpy(&anode,field,sizeof(void *))); //RIGHT, works, same as above line: the contents of struct anyVrml is a pointer
2075  printf("anode._nodeType=%d ",anode->_nodeType);
2076  printf("anyvrml.sfnode._nodetype=%d\n",field->sfnode->_nodeType);
2077  anode = field->sfnode; //RIGHT
2078  printf("anode = anyvrml.sfnode ._nodetype=%d\n",anode->_nodeType);
2079  printf("same?\n");
2080  }
2081  }
2082  push_typed_proxy_fwgetter(ctx, itype, mode, fieldname, field, valueChanged);
2083  break;
2084  }
2085  }
2086  //show_stack(ctx,"in fwgetterNS at end");
2087  return nr;
2088 }
2089 
2090 
2091 int fwgetter0(duk_context *ctx,void *parent,int itype, char *key, int *valueChanged){
2092  //uses fwtype SFNode's getter
2093  FWTYPE *fwt = getFWTYPE(itype);
2094  int jndex, found, nr;
2095  char type, readOnly;
2096  nr = 0;
2097  //check properties - if a property, call the type-specific setter
2098  found = fwhas_generic(fwt,parent,key,&jndex,&type,&readOnly); //SFNode_Iterator
2099  if(found && fwt->Getter){
2100  FWVAL fwretval;
2101  struct X3D_Node *scriptnode;
2102  void *ec = NULL;
2103  //>>just SFNode function getNodeName needs to know the script node context (it can't use its own - it may be an IMPORT)
2104  duk_eval_string(ctx,"__script");
2105  scriptnode = (struct X3D_Node*) duk_to_pointer(ctx,-1);
2106  duk_pop(ctx);
2107  if(scriptnode)
2108  ec = (void *)scriptnode->_executionContext;
2109  //<<
2110 
2111  nr = fwt->Getter(fwt,jndex,ec,parent,&fwretval); //SFNode_Getter
2112  if(nr){
2113  nr = fwval_duk_push(ctx,&fwretval,valueChanged);
2114  }
2115  }
2116  return nr;
2117 }
2118 int fwgetterNS(duk_context *ctx) {
2119  /* when we initializeContext we assign 2 kinds of properties to the global object in the context:
2120  1. FIELDTYPES: our Script Node's dynamic (scene-authored) fields (or more precisely, their js proxys), with features:
2121  - has valueChanged, getName, getMode (getType is part of all FIELDTYPEs and AUXTYPEs)
2122  - reference when getting if non-primitive, deep copy when setting
2123  2. AUXTYPES: a) Browser (AUXTYPE_X3DBrowser) b) X3DConstants (AUXTYPE_X3DConstants)
2124  - reference when getting, never set (these two are static singletons)
2125  */
2126  int nargs, nr;
2127  int rc, itype, *valueChanged = NULL;
2128  //const char *fwName = NULL;
2129  const char *fieldname;
2130  struct X3D_Node *thisScriptNode = NULL;
2131  //union anyVrml *field;
2132 
2133  nargs = duk_get_top(ctx);
2134  itype = 0;
2135  /* retrieve key from nonstandard arg */
2136  //show_stack(ctx,"in fwgetterNS at start");
2137  fieldname = duk_require_string(ctx,0);
2138  //printf("\nfwgetterNS key=%s\n",key);
2139 
2140  /* retrieve field pointer from Cfunc */
2141  duk_push_current_function(ctx);
2142  /* get type of parent object for this property*/
2143  rc = duk_get_prop_string(ctx,-1,"fwItype");
2144  if(rc==1) itype = duk_get_int(ctx,-1);
2145  duk_pop(ctx);
2146  if(itype < AUXTYPE_X3DConstants){
2147  //our script fields
2148  rc = duk_get_prop_string(ctx,-1,"fwNode");
2149  if(rc==1) thisScriptNode = duk_to_pointer(ctx,-1);
2150  duk_pop(ctx);
2151  /* get the pointer to the changed flag */
2152  rc = duk_get_prop_string(ctx,-1,"fwChanged");
2153  if(rc == 1) valueChanged = duk_to_pointer(ctx,-1);
2154  duk_pop(ctx);
2155  }
2156  duk_pop(ctx); //pop current function
2157 
2158 
2159  nr = 0;
2160  if(itype < AUXTYPE_X3DConstants){
2161  //our script fields
2162  union anyVrml any;
2163  any.sfnode = thisScriptNode;
2164  nr = fwgetter0(ctx,&any,FIELDTYPE_SFNode,fieldname,valueChanged);
2165  }else{
2166  //X3DBrowser, X3DConstants
2167  push_typed_proxy_fwgetter(ctx, itype, PKW_initializeOnly, fieldname, NULL, NULL);
2168  nr = 1;
2169  }
2170  return nr;
2171 }
2172 
2173 void add_duk_global_property(duk_context *ctx, int itype, const char *fieldname, int *valueChanged, struct X3D_Node *node ){
2174  //int rc;
2175  //char *str;
2176 
2177  duk_eval_string(ctx, "defineAccessor"); //defineAccessor(obj,propName,setter,getter)
2178  /* push object */
2179  duk_eval_string(ctx,"this"); //global object
2180  /* push key */
2181  duk_push_string(ctx,fieldname); //"myScriptFieldName"
2182  /* push setter */
2183  duk_push_c_function(ctx,fwsetterNS,2); //1 extra parameter is nonstandard (NS) key
2184  if(itype < AUXTYPE_X3DConstants){
2185  duk_push_pointer(ctx,valueChanged);
2186  duk_put_prop_string(ctx,-2,"fwChanged");
2187  duk_push_pointer(ctx,node);
2188  duk_put_prop_string(ctx,-2,"fwNode");
2189  }
2190  duk_push_int(ctx,itype);
2191  duk_put_prop_string(ctx,-2,"fwItype");
2192  /* push getter */
2193  duk_push_c_function(ctx,fwgetterNS,1); //0 extra parameter is nonstandard (NS) key
2194  if(itype < AUXTYPE_X3DConstants){
2195  duk_push_pointer(ctx,node);
2196  duk_put_prop_string(ctx,-2,"fwNode");
2197  duk_push_pointer(ctx,valueChanged);
2198  duk_put_prop_string(ctx,-2,"fwChanged");
2199  }
2200  duk_push_int(ctx,itype);
2201  duk_put_prop_string(ctx,-2,"fwItype");
2202 
2203  duk_call(ctx, 4);
2204  duk_pop(ctx);
2205 }
2206 
2207 void InitScriptField2(struct CRscriptStruct *scriptcontrol, int itype, const char* fieldname, int *valueChanged, struct X3D_Node* parent)
2208 {
2209  /* Creates a javascript-context twin of a Script node for fields of type:
2210  * field/initializeOnly, eventOut/outputOnly, and the field/eventOut part of exposedField/inputOutput
2211  * (not for eventIn/inputOnly, which linked elsewhere to scene author's javascript functions)
2212  * puts the twin as a property on the context's global object
2213  * should make the property 'strict' meaning the property can't be deleted by the script during execution
2214  * but get/set should work normally on the property
2215  * a set should cause a valueChanged flag to be set somewhere, so gatherScriptEventOuts
2216  * can route from eventOut/outputOnly or the eventOut part of exposedField/inputOutput
2217  * InitScriptField2 version: instead of jsNative, hook back into Script_Node->fields[i] for get/set storage
2218  */
2219  duk_context *ctx;
2220  //int iglobal;
2221  //printf("in InitScriptField\n");
2222 
2223  // create twin property
2224  ctx = scriptcontrol->cx;
2225  //iglobal = *(int*)scriptcontrol->glob;
2226  add_duk_global_property(ctx,itype,fieldname, valueChanged,parent);
2227 
2228  return;
2229 }
2230 
2231 void JSInitializeScriptAndFields (int num) {
2232  /* 1. creates javascript-context twins of Script node dynamic/authored fields
2233  2. runs the script as written by the scene author, which has the effect of
2234  declaring all the author's functions (and checking author's syntax)
2235  */
2236  struct Shader_Script *script;
2237  struct ScriptFieldDecl *field;
2238  int i,nfields, kind, itype;
2239  const char *fieldname;
2240  struct CRscriptStruct *scriptcontrol; //*ScriptControlArray,
2241  //ScriptControlArray = getScriptControl();
2242  scriptcontrol = getScriptControlIndex(num); //&ScriptControlArray[num];
2243 
2244 
2245  /* run through fields in order of entry in the X3D file */
2246  script = scriptcontrol->script;
2247  //printf("adding fields from script %p\n",script);
2248  nfields = Shader_Script_getScriptFieldCount(script);
2249  for(i=0;i<nfields;i++){
2250  field = Shader_Script_getScriptField(script,i);
2251  fieldname = ScriptFieldDecl_getName(field);
2252  kind = ScriptFieldDecl_getMode(field);
2253  itype = ScriptFieldDecl_getType(field);
2254  if (kind != PKW_inputOnly) { //we'll hook input events to the author's functions elsewhere
2255  //everything else -fields, eventOuts- needs a strict property twin created on the global object
2256  field->valueChanged = 0;
2257  InitScriptField2(scriptcontrol, itype, fieldname, &field->valueChanged, script->ShaderScriptNode);
2258  }
2259  }
2260 
2261  if (!jsActualrunScript(num, scriptcontrol->scriptText)) {
2262  ConsoleMessage ("JSInitializeScriptAndFields, script failure\n");
2263  scriptcontrol->scriptOK = FALSE;
2264  scriptcontrol->_initialized = TRUE;
2265  return;
2266  }
2267  FREE_IF_NZ(scriptcontrol->scriptText);
2268  scriptcontrol->_initialized = TRUE;
2269  scriptcontrol->scriptOK = TRUE;
2270 
2271  return;
2272 }
2273 
2274 int jsActualrunScript(int num, char *script){
2275  int len, rc, iret;
2276  duk_context *ctx;
2277  int iglobal;
2278  struct CRscriptStruct *ScriptControl; // = getScriptControl();
2279  //printf("in jsActualrunScript\n");
2280 
2281  ScriptControl = getScriptControlIndex(num);
2282  /* get context and global object for this script */
2283  ctx = (duk_context *)ScriptControl->cx;
2284  //iglobal = *((int *)ScriptControl[num].glob);
2285  iglobal = ((int *)&ScriptControl->glob)[0];
2286 
2287  //CLEANUP_JAVASCRIPT(_context)
2288 
2289  len = (int) strlen(script);
2290  iret = TRUE;
2291  if(0){
2292  rc=0;
2293  //this will do a popup abort, with no diagnostic message
2294  duk_eval_string(ctx, script);
2295  if(rc<0){
2296  printf ("ActualrunScript - JS_EvaluateScript failed for %s", script);
2297  printf ("\n");
2298  ConsoleMessage ("ActualrunScript - JS_EvaluateScript failed for %s", script);
2299  iret = FALSE;
2300  }
2301  duk_pop(ctx); //pop result which we don't use
2302  }else{
2303  //this shows the diagnostic message, and allows the program to continue running with the script not run
2304  duk_push_string(ctx, script);
2305  if (duk_peval(ctx) != 0) {
2306  ConsoleMessage("eval failed: %s\n", duk_safe_to_string(ctx, -1));
2307  iret = FALSE;
2308  } else if(0) {
2309  printf("result is: %s\n", duk_safe_to_string(ctx, -1));
2310  }
2311  duk_pop(ctx); //pop result which we don't use
2312  }
2313 
2314 
2315  return iret;
2316 }
2317 void SaveScriptField (int num, indexT kind, indexT type, const char* field, union anyVrml value){
2318  return;
2319 }
2320 static int duk_once = 0;
2321 void process_eventsProcessed(){
2322  duk_context *ctx;
2323  int rc, counter;
2324  struct CRscriptStruct *scriptcontrol;
2325  ttglobal tg;
2326  ppJScript p;
2327 
2328  //if(!duk_once) printf("in process_eventsProcessed\n");
2329  //call function eventsProcessed () {
2330 
2331  duk_once++;
2332 
2333  tg = gglobal();
2334  p = (ppJScript)tg->JScript.prv;
2335  for (counter = 0; counter <= tg->CRoutes.max_script_found_and_initialized; counter++) {
2336  scriptcontrol = getScriptControlIndex(counter);
2337  if(scriptcontrol){
2338  //if (scriptcontrol->eventsProcessed == NULL) {
2339  // //compile function - duktape doesn't have this
2340  // scriptcontrol->eventsProcessed = ???
2341  //}
2342  ctx = scriptcontrol->cx;
2343  if(scriptcontrol->thisScriptType != NOSCRIPT && ctx){
2344  duk_eval_string(ctx,"eventsProcessed"); //gets the evenin function on the stack
2345  //push double TickTime(); as arg
2346  duk_push_number(ctx,TickTime());
2347  rc = duk_pcall(ctx, 1);
2348  if (rc != DUK_EXEC_SUCCESS) {
2349  printf("error: '%s' happened in js function %s called from process_eventsProcessed\n", duk_to_string(ctx, -1),"eventsProcessed");
2350  }
2351  duk_pop(ctx); //pop undefined that results from void myfunc(){}
2352  }
2353  }
2354  }
2355 
2356  return;
2357 }
2358 void js_cleanup_script_context(int counter){
2359  //printf("in js_cleanup_script_context\n");
2360  return;
2361 }
2362 void js_setField_javascriptEventOut_B(union anyVrml* any, int fieldType, unsigned len, int extraData, int actualscript){
2363  //I think in here there is nothing to do for brotos, because the job of _B was to copy values out of javascript and
2364  //into script fields, and the _B broto approach to routing would then do routing from the script fields.
2365  //here in the duk / proxy method, we are already doing the setting of script fields directly.
2366  //printf("in js_setField_javascriptEventOut_B\n");
2367  return;
2368 }
2369 
2370 void setField_javascriptEventOut(struct X3D_Node *tn,unsigned int tptr, int fieldType, unsigned len, int extraData) {
2371  //this proxy method already writes to the script field, so there's nothing to update in javascript
2372  //- can just copy anyVrml from script field to endpoint on Route
2373  // (Brotos don't come in this function)
2374  char *memptr;
2375  char *fromptr;
2376  //int datasize;
2377  ttglobal tg = gglobal();
2378 
2379  /* set up a pointer to where to put this stuff */
2380  memptr = offsetPointer_deref(char *, tn, tptr);
2381  //the from -our current script field value- is coming in through JSglobal_return_val
2382  fromptr = tg->JScript.JSglobal_return_val;
2383 
2384  medium_copy_field0(fieldType,fromptr,memptr); //will copy p data in MF
2385  return;
2386 }
2387 void js_setField_javascriptEventOut(struct X3D_Node *tn,unsigned int tptr, int fieldType, unsigned len, int extraData, int actualscript) {
2388  struct CRscriptStruct *scriptcontrol;
2389 
2390  scriptcontrol = getScriptControlIndex(actualscript);
2391  setField_javascriptEventOut(tn,tptr,fieldType, len, extraData);
2392 }
2393 
2394 
2395 
2396 
2397 void set_one_ECMAtype (int tonode, int toname, int dataType, void *Data, int datalen) {
2398  //char scriptline[100];
2399  //FWVAL newval;
2400  duk_context *ctx;
2401  int obj, rc;
2402  struct CRscriptStruct *ScriptControl; // = getScriptControl();
2403  struct CRjsnameStruct *JSparamnames = getJSparamnames();
2404 
2405  //printf("in set_one_ECMAtype\n");
2406 
2407  #ifdef SETFIELDVERBOSE
2408  printf ("set_one_ECMAtype, to %d namepointer %d, fieldname %s, datatype %d length %d\n",
2409  tonode,toname,JSparamnames[toname].name,dataType,datalen);
2410  #endif
2411 
2412  /* get context and global object for this script */
2413  ScriptControl = getScriptControlIndex(tonode);
2414  ctx = (duk_context *)ScriptControl->cx;
2415  //ctx = (duk_context *)ScriptControl[tonode].cx;
2416  //obj = *(int*)ScriptControl[tonode].glob; //don't need
2417  //obj = ((int*)&ScriptControl[tonode].glob)[0]; //don't need
2418  obj = ((int*)&ScriptControl->glob)[0]; //don't need
2419 
2420 
2421  //get function by name
2422  duk_eval_string(ctx,JSparamnames[toname].name); //gets the evenin function on the stack
2423 
2424  //push ecma value as arg
2425  {
2426  int rc;
2427  FWVAL fwval;
2428  fwval._web3dval.native = Data;
2429  fwval._web3dval.fieldType = dataType;
2430  fwval._web3dval.gc = 0;
2431  fwval.itype = 'W';
2432  rc = fwval_duk_push(ctx, &fwval, NULL);
2433  //if(rc == 1) OK
2434  }
2435  //push double TickTime(); as arg
2436  duk_push_number(ctx,TickTime());
2437  //run function
2438  rc = duk_pcall(ctx, 2); /* [ ... func 2 3 ] -> [ 5 ] */
2439  if (rc != DUK_EXEC_SUCCESS) {
2440  printf("error: '%s' happened in js function %s called from set_one_ECMAType\n", duk_to_string(ctx, -1),JSparamnames[toname].name);
2441  }
2442 
2443  duk_pop(ctx); //pop undefined that results from void myfunc(){}
2444  //printf("end ecma\n");
2445 }
2446 
2447 
2448 
2449 /* setScriptECMAtype called by getField_ToJavascript for
2450  case FIELDTYPE_SFBool:
2451  case FIELDTYPE_SFFloat:
2452  case FIELDTYPE_SFTime:
2453  case FIELDTYPE_SFDouble:
2454  case FIELDTYPE_SFInt32:
2455  case FIELDTYPE_SFString:
2456 */
2457 
2458 void setScriptECMAtype (int num) {
2459  void *fn;
2460  int tptr;
2461  int len;
2462  int to_counter;
2463  CRnodeStruct *to_ptr = NULL;
2464  struct CRStruct *CRoutes = getCRoutes();
2465  struct CRjsnameStruct *JSparamnames = getJSparamnames();
2466  //printf("in setScriptECMAtype\n");
2467  fn = offsetPointer_deref(void *, CRoutes[num].routeFromNode, CRoutes[num].fnptr);
2468  len = CRoutes[num].len;
2469 
2470  for (to_counter = 0; to_counter < CRoutes[num].tonode_count; to_counter++) {
2471  struct Shader_Script *myObj;
2472 
2473  to_ptr = &(CRoutes[num].tonodes[to_counter]);
2474  myObj = X3D_SCRIPT(to_ptr->routeToNode)->__scriptObj;
2475  /* printf ("setScriptECMAtype, myScriptNumber is %d\n",myObj->num); */
2476  tptr = to_ptr->foffset;
2477  set_one_ECMAtype (myObj->num, tptr, JSparamnames[tptr].type, fn,len);
2478  }
2479 }
2480 
2481 void set_one_MultiElementType (int tonode, int tnfield, void *Data, int dataLen){
2482  //tonode - script array num
2483  //tnfield - integer index into jsparamname[] array
2484  //void* Data - pointer to anyVrml of the from node
2485  //datalen - size of anyVrml to memcpy
2486  //FWVAL newval;
2487  duk_context *ctx;
2488  int obj, rc;
2489  int itype;
2490  void *datacopy;
2491  struct CRscriptStruct *ScriptControl; // = getScriptControl();
2492  struct CRjsnameStruct *JSparamnames = getJSparamnames();
2493 
2494  ScriptControl = getScriptControlIndex(tonode);
2495  //ctx = (duk_context *)ScriptControl[tonode].cx;
2496  ctx = (duk_context *)ScriptControl->cx;
2497  //obj = *(int*)ScriptControl[tonode].glob;
2498  //obj = ((int*)&ScriptControl[tonode].glob)[0];
2499  obj = ((int*)&ScriptControl->glob)[0];
2500 
2501  //printf("in set_one_MultiElementType\n");
2502  //get function by name
2503  duk_eval_string(ctx,JSparamnames[tnfield].name); //gets the evenin function on the stack
2504  itype = JSparamnames[tnfield].type;
2505  //medium copy
2506  datacopy = NULL;
2507  medium_copy_field(itype,Data,&datacopy);
2508  push_typed_proxy2(ctx,itype,PKW_inputOutput,datacopy,NULL,'T');
2509  duk_push_number(ctx,TickTime());
2510  //duk_call(ctx,2);
2511  rc = duk_pcall(ctx, 2); /* [ ... func 2 3 ] -> [ 5 ] */
2512  if (rc != DUK_EXEC_SUCCESS) {
2513  printf("error: '%s' happened in js function %s called from set_one_Multi_ElementType\n", duk_to_string(ctx, -1),JSparamnames[tnfield].name);
2514  }
2515  //show_stack(ctx,"after calling isOver");
2516  duk_pop(ctx); //pop undefined that results from void myfunc(){}
2517  return;
2518 }
2519 void set_one_MFElementType(int tonode, int toname, int dataType, void *Data, int datalen){
2520  //tonode - script array num
2521  //tnfield - integer index into jsparamname[] array
2522  //void* Data - MF.p
2523  //datalen - MF.n
2524  //FWVAL newval;
2525  duk_context *ctx;
2526  int obj;
2527  int itype;
2528  union anyVrml *any;
2529  void *datacopy = NULL;
2530  //char *source = (char *)Data - sizeof(int); //backup so we get the whole MF including .n
2531  struct Multi_Any maData;
2532  char *source;
2533  struct CRscriptStruct *ScriptControl; // = getScriptControl();
2534  struct CRjsnameStruct *JSparamnames = getJSparamnames();
2535 
2536  ScriptControl = getScriptControlIndex(tonode);
2537  //ctx = (duk_context *)ScriptControl[tonode].cx;
2538  ctx = (duk_context *)ScriptControl->cx;
2539  //obj = *(int*)ScriptControl[tonode].glob;
2540  //obj = ((int*)&ScriptControl[tonode].glob)[0];
2541  obj = ((int*)&ScriptControl->glob)[0];
2542 
2543  //printf("in set_one_MFElementType\n");
2544  //get function by name
2545  duk_eval_string(ctx,JSparamnames[toname].name); //gets the evenin function on the stack
2546  itype = dataType; //JSparamnames[toname].type;
2547  //medium copy
2548  maData.n = datalen;
2549  maData.p = Data;
2550  source = (char *)&maData;
2551  any = (void*)source;
2552 
2553  medium_copy_field(itype,source,&datacopy);
2554  any = datacopy;
2555  push_typed_proxy2(ctx,itype,PKW_inputOutput,datacopy,NULL,'T');
2556  duk_push_number(ctx,TickTime());
2557  duk_call(ctx,2);
2558  //show_stack(ctx,"after calling isOver");
2559  duk_pop(ctx); //pop undefined that results from void myfunc(){}
2560  return;
2561 }
2562 int jsIsRunning(){
2563  //printf("in jsIsRunning\n");
2564  return 1;
2565 }
2566 void JSDeleteScriptContext(int num){
2567  struct CRscriptStruct *ScriptControl;
2568  //printf("in JSDeleteScriptContext\n");
2569  ScriptControl = getScriptControlIndex(num);
2570  duk_destroy_heap(ScriptControl->cx);
2571  return;
2572 }
2573 void jsShutdown(){
2574  //printf("in jsShutdown\n");
2575  return;
2576 }
2577 void jsClearScriptControlEntries(int num){
2578  //printf("in jsClearScriptControlEntries\n");
2579  return;
2580 }
2581 /* run the script from within Javascript */
2582 /*
2583 int jsrrunScript(duk_context *ctx, char *script, FWval retval) {
2584  double val;
2585  int ival, itype, isOK;
2586  const char *cval;
2587  duk_eval_string(ctx,script);
2588  int RHS_duk_type = duk_get_type(ctx, -1);
2589  isOK = FALSE;
2590  switch(RHS_duk_type){
2591  case DUK_TYPE_NUMBER:
2592  retval->_numeric = duk_require_number(ctx,-1);
2593  retval->itype = 'D';
2594  isOK = TRUE;
2595  break;
2596  case DUK_TYPE_BOOLEAN:
2597  retval->_boolean = duk_require_boolean(ctx,-1);
2598  retval->itype = 'B';
2599  isOK = TRUE;
2600  break;
2601  case DUK_TYPE_STRING:
2602  retval->_string = duk_require_string(ctx,-1);
2603  retval->itype = 'S';
2604  isOK = TRUE;
2605  break;
2606  case DUK_TYPE_OBJECT:
2607  {
2608  int rc, itypeRHS = -1;
2609  union anyVrml *fieldRHS = NULL;
2610  rc = duk_get_prop_string(ctx,-1,"fwItype");
2611  if(rc == 1){
2612  //printf(duk_type_to_string(duk_get_type(ctx, -1)));
2613  itypeRHS = duk_to_int(ctx,-1);
2614  }
2615  duk_pop(ctx);
2616  rc = duk_get_prop_string(ctx,-1,"fwField");
2617  if(rc == 1) fieldRHS = duk_to_pointer(ctx,-1);
2618  duk_pop(ctx);
2619  //we don't need the RHS fwChanged=valueChanged* because we are only changing the LHS
2620 
2621  if(fieldRHS != NULL && itypeRHS > -1){
2622  retval->_web3dval.native = fieldRHS; //shallow copy - won't copy p[] in MF types
2623  retval->_web3dval.fieldType = itypeRHS;
2624  isOK = TRUE;
2625  }
2626  }
2627  break;
2628  case DUK_TYPE_NONE:
2629  case DUK_TYPE_UNDEFINED:
2630  case DUK_TYPE_NULL:
2631  // are we attempting to null out the field? we aren't allowed to change its type (to undefined)
2632  case DUK_TYPE_POINTER:
2633  // don't know what this would be for if anything
2634  default:
2635  isOK = FALSE;
2636  break;
2637  }
2638  duk_pop(ctx); //the duk_eval_string result;
2639  return isOK; //we leave results on stack
2640 }
2641 */
2642 int isScriptControlOK(int actualscript);
2643 int isScriptControlInitialized(int actualscript);
2644 void getField_ToJavascript_B(int shader_num, int fieldOffset, int type, union anyVrml *any, int len);
2645 int runQueuedDirectOutputs()
2646 {
2647  /*
2648  http://www.web3d.org/files/specifications/19775-1/V3.3/Part01/components/scripting.html#directoutputs
2649  http://www.web3d.org/files/specifications/19775-1/V3.3/Part01/components/scripting.html#Accessingfieldsandevents
2650 
2651  Interpretation: The reason the SAI specs say to queue directOutputs in an event queue,
2652  is because external SAIs are running in a different thread: the rendering thread could be
2653  using a node just when you want to write to it from the SAI thread.
2654  I'll assume here we are working on the internal/javascript/ecmascript SAI, and that it is
2655  synchronous with the rendering thread, so it can safely write to nodes without queuing.
2656 
2657  So our effort here is just to make it convenient to write to eventIn/inputOnly
2658  (or the eventIn/inputOnly part of exposedField/inputOutput fields).
2659 
2660  Writing to builtin nodes from a script is already implemented in freewrl by directly writing
2661  the fields immediately during the script. However writing to another script wassn't working properly July 8, 2014.
2662  The following proposed algo was the result of analyzing the behaviour of other vrml/x3d browsers.
2663 
2664  DIRECTOUTPUT ALGO:
2665  When writing to another script node from the current script:
2666  a) write unconditionally to the other script->field->value, including to field/initializeOnly and eventIn/inputOnly
2667  b) set a valueSet flag on the field (like valueChanged for output) and the valueChanged flag
2668  c) set the node _changed or isActive flag to trigger updates
2669  d) either
2670  i) have a stack of queues of script nodes changed and process after each script function OR
2671  ii) like gatherScriptEventOuts() have a spot in the routing loop to look at the valueSet flag
2672  for script fields and if valueSet then if the field is inputOnly/eventIn or exposedField/inputOutput
2673  take the field->value and pass it the the eventIn function (with the current/same timestamp).
2674 
2675  It's this d) ii) we are implementing here.
2676  */
2677  ttglobal tg = gglobal();
2678  struct Shader_Script *script;
2679  struct ScriptFieldDecl *field;
2680  int i,num,kind, itype;
2681  const char *fieldname;
2682  static int doneOnce = 0;
2683  int moreAction;
2684  struct CRscriptStruct *scriptcontrol; //*ScriptControlArray,
2685  //ScriptControlArray = getScriptControl();
2686 
2687  if(!doneOnce){
2688  // printf("in runQueuedDirectOutputs\n");
2689  printf("duktape javascript engine version %ld\n", DUK_VERSION);
2690  doneOnce++;
2691  }
2692  moreAction = FALSE;
2693  for(num=0;num< tg->CRoutes.max_script_found_and_initialized;num++){
2694  scriptcontrol = getScriptControlIndex(num); //&ScriptControlArray[num];
2695  if(scriptcontrol)
2696  {
2697  script = scriptcontrol->script;
2698  if(scriptcontrol->thisScriptType != NOSCRIPT && script){
2699  if(isScriptControlInitialized(script->num) && isScriptControlOK(script->num)){
2700  int nfields = Shader_Script_getScriptFieldCount(script);
2701  for(i=0;i<nfields;i++){
2702  field = Shader_Script_getScriptField(script,i);
2703  fieldname = ScriptFieldDecl_getName(field);
2704  kind = ScriptFieldDecl_getMode(field);
2705  itype = ScriptFieldDecl_getType(field);
2706  if(field->eventInSet){
2707  if( (kind == PKW_inputOnly || kind == PKW_inputOutput)){
2708  int isMF, sftype, len, isize;
2709  int JSparamNameIndex = field->fieldDecl->JSparamNameIndex;
2710  mark_script(script->num);
2711  //run script eventIn function with field->value and tickTime
2712  isMF = itype % 2; //WRONG - use a function to lookup
2713  sftype = itype - isMF;
2714  //from EAI_C_CommonFunctions.c
2715  isize = returnElementLength(sftype) * returnElementRowSize(sftype);
2716  if(isMF) len = sizeof(int) + sizeof(void*);
2717  else len = isize;
2718 
2719  field->eventInSet = FALSE;
2720  getField_ToJavascript_B(script->num, JSparamNameIndex, itype, &field->value, len);
2721  //printf("+eventInSet and input kind=%d value=%f\n",kind,field->value.sffloat);
2722  moreAction = TRUE;
2723  }else{
2724  //printf("-eventInSet but not input kind=%d value=%f\n",kind,field->value.sffloat);
2725  field->eventInSet = FALSE;
2726  }
2727  }
2728  }
2729  }
2730  }
2731  }
2732  }
2733  return moreAction; //IF TRUE will make routing do another loop on the same timestamp
2734 }
2735 
2736 
2737 #endif /* defined(JAVASCRIPT_DUK) */
Definition: FWTYPE.h:64
Definition: FWTYPE.h:112
Definition: Vector.h:36
Definition: Viewer.h:174