1 /**
2 This module implements the __proto__ field given to each special object such as ScriptObject, ScriptFunction,
3 ScriptArray, and ScriptString, as well as the static methods for Object, Array, Function, and String
4 
5 ────────────────────────────────────────────────────────────────────────────────
6 
7 Copyright (C) 2021 pillager86.rf.gd
8 
9 This program is free software: you can redistribute it and/or modify it under 
10 the terms of the GNU General Public License as published by the Free Software 
11 Foundation, either version 3 of the License, or (at your option) any later 
12 version.
13 
14 This program is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
16 PARTICULAR PURPOSE.  See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License along with 
19 this program.  If not, see <https://www.gnu.org/licenses/>.
20 */
21 module mildew.types.bindings;
22 
23 import mildew.environment;
24 import mildew.exceptions;
25 import mildew.interpreter;
26 import mildew.stdlib.buffers;
27 import mildew.stdlib.generator;
28 import mildew.stdlib.regexp;
29 import mildew.types.any;
30 import mildew.types.array;
31 import mildew.types.func;
32 import mildew.types..string;
33 import mildew.types.object;
34 
35 /**
36  * Initializes the bindings of builtin types such as Object, Function, String, and Array. This function is not
37  * required because these objects already have their __proto__ set correctly when constructed.
38  * Documentation for all these classes' methods can be found at https://pillager86.github.io/dmildew/
39  * Params:
40  *  interpreter = The Interpreter instance to load the constructor-namespaces into.
41  */
42 void initializeTypesLibrary(Interpreter interpreter)
43 {
44     ScriptAny Object_ctor = new ScriptFunction("Object", &native_Object_constructor, true);
45     Object_ctor["prototype"] = getObjectPrototype();
46     Object_ctor["prototype"]["constructor"] = Object_ctor;
47     // static Object methods
48     Object_ctor["assign"] = new ScriptFunction("Object.assign", &native_Object_s_assign);
49     Object_ctor["create"] = new ScriptFunction("Object.create", &native_Object_s_create);
50     Object_ctor["defineProperties"] = new ScriptFunction("Object.defineProperties",
51             &native_Object_s_defineProperties);
52     Object_ctor["defineProperty"] = new ScriptFunction("Object.defineProperty",
53             &native_Object_s_defineProperty);
54     Object_ctor["entries"] = new ScriptFunction("Object.entries", &native_Object_s_entries);
55     Object_ctor["fromEntries"] = new ScriptFunction("Object.fromEntries", &native_Object_s_fromEntries);
56     Object_ctor["getOwnPropertyDescriptor"] = new ScriptFunction("Object.getOwnPropertyDescriptor", 
57             &native_Object_s_getOwnPropertyDescriptor);
58     Object_ctor["getOwnPropertyDescriptors"] = new ScriptFunction("Object.getOwnPropertyDescriptors",
59             &native_Object_s_getOwnPropertyDescriptors);
60     Object_ctor["getOwnPropertyNames"] = new ScriptFunction("Object.getOwnPropertyNames", 
61             &native_Object_s_getOwnPropertyNames);
62     Object_ctor["getPrototypeOf"] = new ScriptFunction("Object.getPrototypeOf", &native_Object_s_getPrototypeOf);
63     Object_ctor["is"] = new ScriptFunction("Object.is", &native_Object_s_is);
64     Object_ctor["keys"] = new ScriptFunction("Object.keys", &native_Object_s_keys);
65     Object_ctor["setPrototypeOf"] = new ScriptFunction("Object.setPrototypeOf", &native_Object_s_setPrototypeOf);
66     Object_ctor["values"] = new ScriptFunction("Object.values", &native_Object_s_values);
67 
68     ScriptAny Array_ctor = new ScriptFunction("Array", &native_Array_ctor, true);
69     Array_ctor["prototype"] = getArrayPrototype();
70     Array_ctor["prototype"]["constructor"] = Array_ctor;
71     Array_ctor["from"] = new ScriptFunction("Array.from", &native_Array_s_from);
72     Array_ctor["isArray"] = new ScriptFunction("Array.isArray", &native_Array_s_isArray);
73     Array_ctor["of"] = new ScriptFunction("Array.of", &native_Array_s_of);
74 
75     ScriptAny String_ctor = new ScriptFunction("String", &native_String_ctor, true);
76     String_ctor["prototype"] = getStringPrototype();
77     String_ctor["prototype"]["constructor"] = String_ctor;
78     String_ctor["fromCharCode"] = new ScriptFunction("String.fromCharCode",
79             &native_String_s_fromCharCode);
80     String_ctor["fromCodePoint"] = new ScriptFunction("String.fromCodePoint",
81             &native_String_s_fromCodePoint);
82 
83     // set the consts NaN and Infinity
84     interpreter.forceSetGlobal("Infinity", ScriptAny(double.infinity), true);
85     interpreter.forceSetGlobal("NaN", ScriptAny(double.nan), true);
86 
87     interpreter.forceSetGlobal("Object", Object_ctor, false); // maybe should be const
88     interpreter.forceSetGlobal("Array", Array_ctor, false);
89     interpreter.forceSetGlobal("String", String_ctor, false);
90 }
91 
92 package(mildew):
93 
94 /// Gets the prototype of Object
95 ScriptObject getObjectPrototype()
96 {
97     if(_objectPrototype is null)
98     {
99         _objectPrototype = new ScriptObject("object"); // this is the base prototype for all objects
100         _objectPrototype["hasOwnProperty"] = new ScriptFunction("Object.prototype.hasOwnProperty",
101                 &native_Object_hasOwnProperty);
102         _objectPrototype["isPrototypeOf"] = new ScriptFunction("Object.prototype.isPrototypeOf",
103                 &native_Object_isPrototypeOf);
104         _objectPrototype["toString"] = new ScriptFunction("Object.prototype.toString", &native_Object_toString);
105     }
106     return _objectPrototype;
107 }
108 
109 /// Gets the prototype of all arrays
110 ScriptObject getArrayPrototype()
111 {
112     if(_arrayPrototype is null)
113     {
114         _arrayPrototype = new ScriptObject("array", null);
115         _arrayPrototype["at"] = new ScriptFunction("Array.prototype.at", &native_Array_at);
116         _arrayPrototype["concat"] = new ScriptFunction("Array.prototype.concat", &native_Array_concat);
117         _arrayPrototype["copyWithin"] = new ScriptFunction("Array.prototype.copyWithin",
118                 &native_Array_copyWithin);
119         _arrayPrototype["entries"] = new ScriptFunction("Array.prototype.entries", &native_Array_entries);
120         _arrayPrototype["every"] = new ScriptFunction("Array.prototype.every", &native_Array_every);
121         _arrayPrototype["fill"] = new ScriptFunction("Array.prototype.fill", &native_Array_fill);
122         _arrayPrototype["filter"] = new ScriptFunction("Array.prototype.filter", &native_Array_filter);
123         _arrayPrototype["find"] = new ScriptFunction("Array.prototype.find", &native_Array_find);
124         _arrayPrototype["findIndex"] = new ScriptFunction("Array.prototype.findIndex", &native_Array_findIndex);
125         _arrayPrototype["flat"] = new ScriptFunction("Array.prototype.flat", &native_Array_flat);
126         _arrayPrototype["flatMap"] = new ScriptFunction("Array.prototype.flatMap", &native_Array_flatMap);
127         _arrayPrototype["forEach"] = new ScriptFunction("Array.prototype.forEach", &native_Array_forEach);
128         _arrayPrototype["includes"] = new ScriptFunction("Array.prototype.includes", &native_Array_includes);
129         _arrayPrototype["indexOf"] = new ScriptFunction("Array.prototype.indexOf", &native_Array_indexOf);
130         _arrayPrototype["join"] = new ScriptFunction("Array.prototype.join", &native_Array_join);
131         _arrayPrototype["keys"] = new ScriptFunction("Array.prototype.keys", &native_Array_keys);
132         _arrayPrototype["lastIndexOf"] = new ScriptFunction("Array.prototype.lastIndexOf",
133                 &native_Array_lastIndexOf);
134         _arrayPrototype.addGetterProperty("length", new ScriptFunction("Array.prototype.length", 
135                 &native_Array_p_length));
136         _arrayPrototype.addSetterProperty("length", new ScriptFunction("Array.prototype.length", 
137                 &native_Array_p_length));
138         _arrayPrototype["map"] = new ScriptFunction("Array.prototype.map", &native_Array_map);
139         _arrayPrototype["pop"] = new ScriptFunction("Array.prototype.pop", &native_Array_pop);
140         _arrayPrototype["push"] = new ScriptFunction("Array.prototype.push", &native_Array_push);
141         _arrayPrototype["reduce"] = new ScriptFunction("Array.prototype.reduce", &native_Array_reduce);
142         _arrayPrototype["reduceRight"] = new ScriptFunction("Array.prototype.reduceRight",
143                 &native_Array_reduceRight);
144         _arrayPrototype["reverse"] = new ScriptFunction("Array.prototype.reverse", &native_Array_reverse);
145         _arrayPrototype["shift"] = new ScriptFunction("Array.prototype.shift", &native_Array_shift);
146         _arrayPrototype["slice"] = new ScriptFunction("Array.prototype.slice", &native_Array_slice);
147         _arrayPrototype["some"] = new ScriptFunction("Array.prototype.some", &native_Array_some);
148         _arrayPrototype["sort"] = new ScriptFunction("Array.prototype.sort", &native_Array_sort);
149         _arrayPrototype["splice"] = new ScriptFunction("Array.prototype.splice", &native_Array_splice);
150         _arrayPrototype["unshift"] = new ScriptFunction("Array.prototype.unshift", &native_Array_unshift);
151         _arrayPrototype["values"] = new ScriptFunction("Array.prototype.values", &native_Array_values);
152     }
153     return _arrayPrototype;
154 }
155 
156 /// Gets the prototype of all functions
157 ScriptObject getFunctionPrototype()
158 {
159     import mildew.exceptions: ScriptRuntimeException;
160     if(_functionPrototype is null)
161     {
162         _functionPrototype = new ScriptObject("function", null);
163         _functionPrototype["apply"] = new ScriptFunction("Function.prototype.apply", &native_Function_apply);
164         _functionPrototype["bind"] = new ScriptFunction("Function.prototype.bind", &native_Function_bind);
165         _functionPrototype["call"] = new ScriptFunction("Function.prototype.call", &native_Function_call);
166         _functionPrototype.addGetterProperty("isGenerator", new ScriptFunction("Function.prototype.isGenerator",
167                 &native_Function_p_isGenerator));
168         _functionPrototype.addGetterProperty("isNative", new ScriptFunction("Function.prototype.isNative",
169                 &native_Function_p_isNative));
170         _functionPrototype.addGetterProperty("length", new ScriptFunction("Function.prototype.length",
171                 &native_Function_p_length));
172         _functionPrototype.addGetterProperty("name", new ScriptFunction("Function.prototype.name",
173                 &native_Function_p_name));
174     }
175     return _functionPrototype;
176 }
177 
178 /// Gets the prototype of all strings
179 ScriptObject getStringPrototype()
180 {
181     if(_stringPrototype is null)
182     {
183         _stringPrototype = new ScriptObject("string", null);
184         _stringPrototype["charAt"] = new ScriptFunction("String.prototype.charAt", &native_String_charAt);
185         _stringPrototype["charCodeAt"] = new ScriptFunction("String.prototype.charCodeAt", 
186                 &native_String_charCodeAt);
187         _stringPrototype["codePointAt"] = new ScriptFunction("String.prototype.codePointAt",
188                 &native_String_codePointAt);
189         _stringPrototype["concat"] = new ScriptFunction("String.prototype.concat", &native_String_concat);
190         _stringPrototype["endsWith"] = new ScriptFunction("String.prototype.endsWith", &native_String_endsWith);
191         _stringPrototype["includes"] = new ScriptFunction("String.prototype.includes", &native_String_includes);
192         _stringPrototype["indexOf"] = new ScriptFunction("String.prototype.indexOf", &native_String_indexOf);
193         _stringPrototype["lastIndexOf"] = new ScriptFunction("String.prototype.lastIndexOf",
194                 &native_String_lastIndexOf);
195         _stringPrototype.addGetterProperty("length", new ScriptFunction("String.prototype.length", 
196                 &native_String_p_length));
197         _stringPrototype["match"] = new ScriptFunction("String.prototype.match", &native_String_match);
198         _stringPrototype["matchAll"] = new ScriptFunction("String.prototype.matchAll", &native_String_matchAll);
199         _stringPrototype["normalize"] = new ScriptFunction("String.prototype.normalize",
200                 &native_String_normalize);
201         _stringPrototype["padEnd"] = new ScriptFunction("String.prototype.padEnd", &native_String_padEnd);
202         _stringPrototype["padStart"] = new ScriptFunction("String.prototype.padStart",
203                 &native_String_padStart);
204         _stringPrototype["repeat"] = new ScriptFunction("String.prototype.repeat", &native_String_repeat);
205         _stringPrototype["replace"] = new ScriptFunction("String.prototype.replace", &native_String_replace);
206         _stringPrototype["replaceAll"] = new ScriptFunction("String.prototype.replaceAll",
207                 &native_String_replaceAll);
208         _stringPrototype["search"] = new ScriptFunction("String.prototype.search", &native_String_search);
209         _stringPrototype["slice"] = new ScriptFunction("String.prototype.slice", &native_String_slice);
210         _stringPrototype["split"] = new ScriptFunction("String.prototype.split", &native_String_split);
211         _stringPrototype["startsWith"] = new ScriptFunction("String.prototype/startsWith", &native_String_startsWith);
212         _stringPrototype["substring"] = new ScriptFunction("String.prototype.substring", &native_String_substring);
213         _stringPrototype["toLowerCase"] = new ScriptFunction("String.prototype.toLowerCase",
214                 &native_String_toLowerCase);
215         _stringPrototype["toUpperCase"] = new ScriptFunction("String.prototype.toUpperCase", 
216                 &native_String_toUpperCase);
217     }
218     return _stringPrototype;
219 }
220 
221 private ScriptObject _objectPrototype;
222 private ScriptObject _arrayPrototype;
223 private ScriptObject _functionPrototype;
224 private ScriptObject _stringPrototype;
225 
226 // Helper methods
227 
228 /// Determine the local this
229 ScriptAny getLocalThis(Environment env, ScriptAny func=ScriptAny.UNDEFINED)
230 {
231     if(func && func.type == ScriptAny.Type.FUNCTION)
232     {
233         auto fn = func.toValue!ScriptFunction;
234         if(fn.boundThis != ScriptAny.UNDEFINED)
235             return fn.boundThis;
236     }
237     bool _; // @suppress(dscanner.suspicious.unmodified)
238     ScriptAny* thisObj = env.lookupVariableOrConst("this", _);
239     if(thisObj == null)
240         return ScriptAny.UNDEFINED;
241     return *thisObj;
242 }
243 
244 /// Returns true if object is an iterable for Array.from
245 bool isIterable(ScriptAny object)
246 {
247     import mildew.stdlib.buffers: AbstractArrayBuffer;
248     if (
249         object.type == ScriptAny.Type.ARRAY ||
250         object.type == ScriptAny.Type.STRING ||
251         object.isNativeObjectType!ScriptGenerator
252     )
253     {
254         return true;
255     }
256     else if(object.isNativeObjectType!AbstractArrayBuffer)
257     {
258         return object.toNativeObject!AbstractArrayBuffer.isView;
259     }
260     return false;
261 }
262 
263 //
264 // Object methods /////////////////////////////////////////////////////////////
265 //
266 
267 private ScriptAny native_Object_constructor(Environment env, ScriptAny* thisObj, ScriptAny[] args, 
268         ref NativeFunctionError nfe)
269 {
270     if(args.length >= 1)
271     {
272         if(args[0].isObject)
273             *thisObj = args[0];
274     }
275     return ScriptAny.UNDEFINED;
276 }
277 
278 private ScriptAny native_Object_s_assign(Environment env, ScriptAny* thisObj,
279                                        ScriptAny[] args, ref NativeFunctionError nfe)
280 {
281     if(args.length < 2)
282         return ScriptAny.UNDEFINED;
283     if(!args[0].isObject || !args[1].isObject)
284         return ScriptAny.UNDEFINED;
285     auto objA = args[0].toValue!ScriptObject;
286     auto objB = args[1].toValue!ScriptObject;
287     foreach(k, v ; objB.dictionary)
288     {
289         objA.assignField(k, v);
290     }
291     foreach(k, v ; objB.getters)
292     {
293         objA.addGetterProperty(k, v);
294     }    
295     foreach(k, v ; objB.setters)
296     {
297         objA.addSetterProperty(k, v);
298     }
299     return ScriptAny(objA);
300 }
301 
302 /**
303  * Object.create: This can be called by the script to create a new object whose prototype is the
304  * parameter.
305  */
306 private ScriptAny native_Object_s_create(Environment env,
307                                         ScriptAny* thisObj, 
308                                         ScriptAny[] args, 
309                                         ref NativeFunctionError nfe)
310 {
311     if(args.length < 1)
312     {
313         nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS;
314         return ScriptAny.UNDEFINED;
315     }
316 
317     if(!args[0].isObject)
318     {
319         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
320         return ScriptAny.UNDEFINED;
321     }
322 
323     auto newObj = new ScriptObject("", args[0].toValue!ScriptObject);
324 
325     return ScriptAny(newObj);
326 }
327 
328 private ScriptAny native_Object_s_defineProperties(Environment env, ScriptAny* thisObj,
329                                                    ScriptAny[] args, ref NativeFunctionError nfe)
330 {
331     if(args.length < 2 || !args[0].isObject || !args[1].isObject)
332         return ScriptAny.UNDEFINED;
333     auto obj = args[0].toValue!ScriptObject;
334     auto propDesc = args[1].toValue!ScriptObject;
335     foreach(k,v ; propDesc.dictionary)
336     {
337         if(v.isObject)
338         {
339             auto data = v.toValue!ScriptObject;
340             if(data.hasOwnFieldOrProperty("value"))
341             {
342                 obj[k] = data["value"];
343             }
344             else
345             {
346                 if(data.hasOwnFieldOrProperty("get"))
347                 {
348                     auto getter = data["get"].toValue!ScriptFunction;
349                     if(getter is null)
350                     {
351                         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
352                         return ScriptAny.UNDEFINED;
353                     }
354                     obj.addGetterProperty(k, getter);
355                 }
356                 if(data.hasOwnFieldOrProperty("set"))
357                 {
358                     auto setter = data["set"].toValue!ScriptFunction;
359                     if(setter is null)
360                     {
361                         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
362                         return ScriptAny.UNDEFINED;
363                     }
364                     obj.addSetterProperty(k, setter);
365                 }
366             }
367         }
368         else
369         {
370             nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
371             return ScriptAny.UNDEFINED;
372         }
373     }
374     return ScriptAny(obj);
375 }
376 
377 private ScriptAny native_Object_s_defineProperty(Environment env, ScriptAny* thisObj,
378                                                  ScriptAny[] args, ref NativeFunctionError nfe)
379 {
380     if(args.length < 3 || !args[0].isObject || !args[2].isObject)
381         return ScriptAny.UNDEFINED;
382     auto obj = args[0].toValue!ScriptObject;
383     auto propName = args[1].toString();
384     auto propDef = args[2].toValue!ScriptObject;
385 
386     if(propDef.hasOwnFieldOrProperty("value"))
387     {
388         obj[propName] = propDef["value"];
389     }
390     else
391     {
392         if(propDef.hasOwnFieldOrProperty("get"))
393         {
394             auto fn = propDef["get"].toValue!ScriptFunction;
395             if(fn is null)
396             {
397                 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
398                 return ScriptAny.UNDEFINED;
399             }
400             obj.addGetterProperty(propName, fn);
401         }
402         if(propDef.hasOwnFieldOrProperty("set"))
403         {
404             auto fn = propDef["set"].toValue!ScriptFunction;
405             if(fn is null)
406             {
407                 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
408                 return ScriptAny.UNDEFINED;
409             }
410             obj.addSetterProperty(propName, fn);
411         }
412     }
413 
414     return ScriptAny(obj);
415 }
416 
417 /// Returns an array of 2-element arrays representing the key and value of each dictionary entry
418 private ScriptAny native_Object_s_entries(Environment env,
419                                         ScriptAny* thisObj,
420                                         ScriptAny[] args,
421                                         ref NativeFunctionError nfe)
422 {
423     if(args.length < 1)
424         return ScriptAny.UNDEFINED;
425     
426     if(!args[0].isObject)
427         return ScriptAny.UNDEFINED;
428     
429     ScriptAny[][] entries;
430     foreach(key, value ; args[0].toValue!ScriptObject.dictionary)
431     {
432         entries ~= [ScriptAny(key), value];
433     }
434     return ScriptAny(entries);
435 }
436 
437 // TODO s_freeze, which will require modification of VM's opObjGet and opObjSet
438 
439 private ScriptAny native_Object_s_fromEntries(Environment env, ScriptAny* thisObj,
440                                               ScriptAny[] args, ref NativeFunctionError nfe)
441 {
442     if(args.length < 1 || args[0].type != ScriptAny.Type.ARRAY)
443         return ScriptAny.UNDEFINED;
444     auto arr = args[0].toValue!(ScriptAny[]);
445     auto obj = new ScriptObject("object", null);
446     foreach(element ; arr)
447     {
448         if(element.type == ScriptAny.Type.ARRAY)
449         {
450             auto keyValue = element.toValue!(ScriptAny[]);
451             if(keyValue.length >= 2)
452             {
453                 obj[keyValue[0].toString()] = keyValue[1];
454             }
455         }
456     }
457     return ScriptAny(obj);
458 }
459 
460 /// Returns a possible getter or setter or value for an object
461 private ScriptAny native_Object_s_getOwnPropertyDescriptor(Environment env,
462                                                         ScriptAny* thisObj,
463                                                         ScriptAny[] args,
464                                                         ref NativeFunctionError nfe)
465 {
466     if(args.length < 2)
467         return ScriptAny.UNDEFINED;
468     if(!args[0].isObject)
469         return ScriptAny.UNDEFINED;
470     auto propName = args[1].toString();
471     return ScriptAny(args[0].toValue!ScriptObject.getOwnPropertyOrFieldDescriptor(propName));
472 }
473 
474 private ScriptAny native_Object_s_getOwnPropertyDescriptors(Environment env, ScriptAny* thisObj,
475                                                             ScriptAny[] args, ref NativeFunctionError nfe)
476 {
477     if(args.length < 1 || !args[0].isObject)
478         return ScriptAny.UNDEFINED;
479     auto obj = args[0].toValue!ScriptObject;
480     return ScriptAny(obj.getOwnFieldOrPropertyDescriptors());
481 }
482 
483 private ScriptAny native_Object_s_getOwnPropertyNames(Environment env, ScriptAny* thisObj,
484                                                       ScriptAny[] args, ref NativeFunctionError nfe)
485 {
486     if(args.length < 1 || !args[0].isObject)
487         return ScriptAny.UNDEFINED;
488     auto obj = args[0].toValue!ScriptObject;
489     bool[string] map;
490     foreach(k,v ; obj.dictionary)
491         map[k] = true;
492     foreach(k,v ; obj.getters)
493         map[k] = true;
494     foreach(k,v ; obj.setters)
495         map[k] = true;
496     return ScriptAny(map.keys);
497 }
498 
499 // not sure about Object.getOwnPropertySymbols
500 
501 private ScriptAny native_Object_s_getPrototypeOf(Environment env, ScriptAny* thisObj,
502                                                  ScriptAny[] args, ref NativeFunctionError nfe)
503 {
504     if(args.length < 1 || !args[0].isObject)
505         return ScriptAny.UNDEFINED;
506     auto obj = args[0].toValue!ScriptObject;
507     return ScriptAny(obj.prototype);
508 }
509 
510 private ScriptAny native_Object_hasOwnProperty(Environment env, ScriptAny* thisObj,
511                                                ScriptAny[] args, ref NativeFunctionError nfe)
512 {
513     if(!thisObj.isObject)
514         return ScriptAny.UNDEFINED;
515     auto obj = thisObj.toValue!ScriptObject;
516     if(args.length < 1)
517         return ScriptAny.UNDEFINED;
518     auto propName = args[0].toString();
519     return ScriptAny(obj.hasOwnFieldOrProperty(propName));
520 }
521 
522 private ScriptAny native_Object_s_is(Environment env, ScriptAny* thisObj,
523                                      ScriptAny[] args, ref NativeFunctionError nfe)
524 {
525     /// Two non-reference values can never "is" each other
526     if(args.length < 2 || !args[0].isObject || !args[1].isObject)
527         return ScriptAny(false);
528     auto objA = args[0].toValue!ScriptObject;
529     auto objB = args[1].toValue!ScriptObject;
530     return ScriptAny(objA is objB);
531 }
532 
533 // Object.isExtensible doesn't fit the design yet
534 
535 // Object.isFrozen doesn't fit the design yet
536 
537 private ScriptAny native_Object_isPrototypeOf(Environment env, ScriptAny* thisObj,
538                                               ScriptAny[] args, ref NativeFunctionError nfe)
539 {
540     if(!thisObj.isObject)
541         return ScriptAny(false);
542     if(args.length < 1 || !args[0].isObject)
543         return ScriptAny(false);
544     auto objProto = thisObj.toValue!ScriptObject;
545     auto objWithProto = args[0].toValue!ScriptObject;
546     return ScriptAny(objProto is objWithProto.prototype);
547 }
548 
549 // Object.isSealed doesn't really apply yet
550 
551 /// returns an array of keys of an object (or function)
552 private ScriptAny native_Object_s_keys(Environment env,
553                                     ScriptAny* thisObj,
554                                     ScriptAny[] args,
555                                     ref NativeFunctionError nfe)
556 {
557     if(args.length < 1)
558         return ScriptAny.UNDEFINED;
559     
560     if(!args[0].isObject)
561         return ScriptAny.UNDEFINED;
562 
563     auto sobj = args[0].toValue!ScriptObject;
564     auto keys = ScriptAny(sobj.dictionary.keys);
565     return keys;
566 }
567 
568 // Object.preventExtensions doesn't really apply yet
569 
570 // Object.prototype.propertyIsEnumerable doesn't really apply, and may never will
571 
572 // Object.seal doesn't really apply yet. Maybe someday
573 
574 private ScriptAny native_Object_s_setPrototypeOf(Environment env, ScriptAny* thisObj,
575                                                  ScriptAny[] args, ref NativeFunctionError nfe)
576 {
577     if(args.length < 2 || !args[0].isObject)
578         return ScriptAny.UNDEFINED;
579     auto objToSet = args[0].toValue!ScriptObject;
580     auto newProto = args[1].toValue!ScriptObject; // @suppress(dscanner.suspicious.unmodified)
581     // this may set a null prototype if newProto is null
582     objToSet.prototype = newProto;
583     return args[0];
584 }
585 
586 private ScriptAny native_Object_toString(Environment env, ScriptAny* thisObj,
587                                          ScriptAny[] args, ref NativeFunctionError nfe)
588 {
589     if(!thisObj.isObject)
590         return ScriptAny.UNDEFINED;
591     return ScriptAny(thisObj.toString());
592 }
593 
594 // Object.valueOf does not apply and will NEVER apply because we do not allow the "boxing" of primitive
595 //  values for no reason.
596 
597 /// returns an array of values of an object (or function)
598 private ScriptAny native_Object_s_values(Environment env,
599                                         ScriptAny* thisObj,
600                                         ScriptAny[] args,
601                                         ref NativeFunctionError nfe)
602 {
603     if(args.length < 1)
604         return ScriptAny.UNDEFINED;
605     
606     if(!args[0].isObject)
607         return ScriptAny.UNDEFINED;
608 
609     auto sobj = args[0].toValue!ScriptObject;
610     auto values = ScriptAny(sobj.dictionary.values);
611     return values;
612 }
613 
614 //
615 // Array methods //////////////////////////////////////////////////////////////
616 //
617 
618 private ScriptAny native_Array_ctor(Environment env, ScriptAny* thisObj,
619                                     ScriptAny[] args, ref NativeFunctionError nfe)
620 {
621     ScriptAny[] result;
622     if(args.length == 1 && args[0].type == ScriptAny.Type.INTEGER)
623     {
624         result = new ScriptAny[args[0].toValue!long];
625     }
626     else
627     {
628         foreach(arg ; args)
629             result ~= arg;
630     }
631     *thisObj = ScriptAny(result);
632     return ScriptAny.UNDEFINED;
633 }
634 
635 private ScriptAny native_Array_at(Environment env, ScriptAny* thisObj,
636                                   ScriptAny[] args, ref NativeFunctionError nfe)
637 {
638     if(thisObj.type != ScriptAny.Type.ARRAY)
639         return ScriptAny.UNDEFINED;
640     if(args.length < 1)
641         return ScriptAny.UNDEFINED;
642     auto array = thisObj.toValue!(ScriptAny[]);
643     long index = args[0].toValue!long;
644     if(index < 0)
645         index += array.length;
646     if(index < 0 || index >= array.length)
647         return ScriptAny.UNDEFINED;
648     return array[index];
649 }
650 
651 private ScriptAny native_Array_concat(Environment env, ScriptAny* thisObj, 
652                                       ScriptAny[] args, ref NativeFunctionError nfe)
653 {
654     if(thisObj.type != ScriptAny.Type.ARRAY)
655         return ScriptAny.UNDEFINED;
656     if(args.length < 1)
657         return *thisObj;
658     ScriptAny[] result = thisObj.toValue!ScriptArray.array;
659     foreach(arg ; args)
660     {
661         if(arg.type != ScriptAny.Type.ARRAY)
662         {
663             result ~= arg;
664         }
665         else
666         {
667             result ~= arg.toValue!ScriptArray.array;
668         }
669     }
670     return ScriptAny(result);
671 }
672 
673 private ScriptAny native_Array_copyWithin(Environment env, ScriptAny* thisObj,
674                                           ScriptAny[] args, ref NativeFunctionError nfe)
675 {
676     if(thisObj.type != ScriptAny.Type.ARRAY)
677         return ScriptAny.UNDEFINED;
678     auto arr = thisObj.toValue!ScriptArray;
679     long target = args.length > 0 ? args[0].toValue!long : arr.array.length;
680     long start = args.length > 1 ? args[1].toValue!long : 0;
681     long end = args.length > 2 ? args[2].toValue!long : arr.array.length;
682 
683     if(target < 0) target += arr.array.length;
684     if(start < 0) start += arr.array.length;
685     if(end < 0) end += arr.array.length;
686 
687     if(target < 0 || target >= arr.array.length)
688         target = arr.array.length;
689     if(start < 0 || start >= arr.array.length)
690         start  = 0;
691     if(end < 0 || end >= arr.array.length)
692         end = arr.array.length;
693     if(end <= start)
694         return *thisObj;
695     for(long i = 0; i < (end - start); ++i)
696     {
697         if(i + target >= arr.array.length || i + start >= arr.array.length)
698             break;
699         arr.array[i+target] = arr.array[i+start];
700     }
701     return *thisObj;
702 }
703 
704 private ScriptAny native_Array_entries(Environment env, ScriptAny* thisObj,
705                                        ScriptAny[] args, ref NativeFunctionError nfe)
706 {
707     import std.concurrency: yield;
708 
709     if(thisObj.type != ScriptAny.Type.ARRAY)
710         return ScriptAny.UNDEFINED;
711     auto genFunc = new ScriptFunction("Iterator", 
712         delegate ScriptAny(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe)
713         {
714             auto arr = args[0].toValue!(ScriptAny[]);
715             foreach(index, value ; arr)
716             {
717                 auto entry = new ScriptAny[2];
718                 entry[0] = ScriptAny(index);
719                 entry[1] = ScriptAny(value);
720                 yield!ScriptAny(ScriptAny(entry));
721             }
722             return ScriptAny.UNDEFINED;
723         }
724     );
725     auto generator = new ScriptGenerator(env, genFunc, [*thisObj]);
726     auto iterator = new ScriptObject("Iterator", getGeneratorPrototype, generator);
727     return ScriptAny(iterator);
728 }
729 
730 private ScriptAny native_Array_every(Environment env, ScriptAny* thisObj,
731                                      ScriptAny[] args, ref NativeFunctionError nfe)
732 {
733     if(thisObj.type != ScriptAny.Type.ARRAY)
734         return ScriptAny(false);
735     auto arr = thisObj.toValue!ScriptArray;
736     if(args.length < 1)
737         return ScriptAny(false);
738     if(args[0].type != ScriptAny.Type.FUNCTION)
739         return ScriptAny(false);
740     auto theThisArg = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
741     bool result = true;
742     size_t counter = 0;
743     foreach(element ; arr.array)
744     {
745         auto temp = native_Function_call(env, &args[0], 
746             [ theThisArg, element, ScriptAny(counter), *thisObj ], nfe);
747         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
748             return temp;
749         result = result && temp;
750         if(!result)
751             return ScriptAny(result);
752         ++counter;
753     }
754     return ScriptAny(result);
755 }
756 
757 private ScriptAny native_Array_fill(Environment env, ScriptAny* thisObj,
758                                     ScriptAny[] args, ref NativeFunctionError nfe)
759 {
760     if(thisObj.type != ScriptAny.Type.ARRAY)
761         return ScriptAny.UNDEFINED;
762     auto arr = thisObj.toValue!ScriptArray.array;
763     if(args.length < 1)
764         return *thisObj;
765     // auto value = args[0]; // @suppress(dscanner.suspicious.unmodified)
766     long start = args.length > 1 ? args[1].toValue!long : 0;
767     long end = args.length > 2 ? args[2].toValue!long : arr.length;
768 
769     if(start < 0) start += arr.length;
770     if(end < 0) end += arr.length;
771 
772     if(start < 0 || start >= arr.length)
773         start = 0;
774     if(end < 0 || end >= arr.length)
775         end = arr.length;
776     for(size_t i = start; i < end; ++i)
777         arr[i] = args[0];
778     return *thisObj;
779 }
780 
781 private ScriptAny native_Array_filter(Environment env, ScriptAny* thisObj,
782                                       ScriptAny[] args, ref NativeFunctionError nfe)
783 {
784     if(thisObj.type != ScriptAny.Type.ARRAY)
785         return ScriptAny.UNDEFINED;
786     auto arr = thisObj.toValue!ScriptArray.array;
787     if(args.length < 1)
788         return *thisObj;
789     if(args[0].type != ScriptAny.Type.FUNCTION)
790         return *thisObj;
791     ScriptAny thisToUse = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
792     ScriptAny[] result;
793     size_t counter = 0;
794     foreach(element ; arr)
795     {
796         auto temp = native_Function_call(env, &args[0], 
797             [thisToUse, element, ScriptAny(counter), *thisObj], nfe);
798         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
799             return temp;
800         if(temp)
801             result ~= element;
802         ++counter;
803     }
804     return ScriptAny(result);
805 }
806 
807 private ScriptAny native_Array_find(Environment env, ScriptAny* thisObj, 
808                                     ScriptAny[] args, ref NativeFunctionError nfe)
809 {
810     if(thisObj.type != ScriptAny.Type.ARRAY)
811         return ScriptAny.UNDEFINED;
812     auto arr = thisObj.toValue!ScriptArray.array;
813     if(args.length < 1)
814         return ScriptAny.UNDEFINED;
815     if(args[0].type != ScriptAny.Type.FUNCTION)
816         return ScriptAny.UNDEFINED;
817     auto thisToUse = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
818     for(size_t i = 0; i < arr.length; ++i)
819     {
820         auto temp = native_Function_call(env, &args[0], 
821             [thisToUse, arr[i], ScriptAny(i), *thisObj], nfe);
822         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
823             return temp;
824         if(temp)
825             return arr[i];
826     }
827 
828     return ScriptAny.UNDEFINED;
829 }
830 
831 private ScriptAny native_Array_findIndex(Environment env, ScriptAny* thisObj, 
832                                     ScriptAny[] args, ref NativeFunctionError nfe)
833 {
834     if(thisObj.type != ScriptAny.Type.ARRAY)
835         return ScriptAny.UNDEFINED;
836     auto arr = thisObj.toValue!ScriptArray.array;
837     if(args.length < 1)
838         return ScriptAny.UNDEFINED;
839     if(args[0].type != ScriptAny.Type.FUNCTION)
840         return ScriptAny.UNDEFINED;
841     auto thisToUse = args.length > 1 ? args[1] : ScriptAny.UNDEFINED;
842     for(size_t i = 0; i < arr.length; ++i)
843     {
844         auto temp = native_Function_call(env, &args[0], 
845             [thisToUse, arr[i], ScriptAny(i), *thisObj], nfe);
846         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
847             return temp;
848         if(temp)
849             return ScriptAny(i);
850     }
851 
852     return ScriptAny(-1);
853 }
854 
855 // Credit for flat and flatMap algorithm:
856 // https://medium.com/better-programming/javascript-tips-4-array-flat-and-flatmap-implementation-2f81e618bde
857 private ScriptAny native_Array_flat(Environment env, ScriptAny* thisObj,
858                                     ScriptAny[] args, ref NativeFunctionError nfe)
859 {
860     if(thisObj.type != ScriptAny.Type.ARRAY)
861         return ScriptAny.UNDEFINED;
862     auto arr = thisObj.toValue!ScriptArray.array;
863     ScriptAny[] flattened;
864     immutable depth = args.length > 0 ? args[0].toValue!int : 1;
865     void flattener(ScriptAny[] list, int dp)
866     {
867         foreach(item ; list)
868         {
869             if(item.type == ScriptAny.Type.ARRAY && dp > 0)
870             {
871                 flattener(item.toValue!(ScriptAny[]), dp - 1);
872             }
873             else
874             {
875                 flattened ~= item;
876             }
877         }
878     }
879     flattener(arr, depth);
880     return ScriptAny(flattened);
881 }
882 
883 private ScriptAny native_Array_flatMap(Environment env, ScriptAny* thisObj,
884                                        ScriptAny[] args, ref NativeFunctionError nfe)
885 {
886     if(thisObj.type != ScriptAny.Type.ARRAY)
887         return ScriptAny.UNDEFINED;
888     auto arr = thisObj.toValue!ScriptArray.array;
889     ScriptAny[] flattened;
890     if(args.length < 1)
891         return *thisObj;
892     if(args[0].type != ScriptAny.Type.FUNCTION)
893         return *thisObj;
894     ScriptAny thisToUse = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
895     for(size_t i = 0; i < arr.length; ++i)
896     {
897         auto temp = native_Function_call(env, &args[0], [thisToUse, arr[i], ScriptAny(i), *thisObj], nfe);
898         if(env.g.interpreter.vm.hasException)
899             return temp;
900         if(nfe != NativeFunctionError.NO_ERROR)
901             return temp;
902         if(temp.type == ScriptAny.Type.ARRAY)
903         {
904             foreach(element ; temp.toValue!ScriptArray.array)
905                 flattened ~= element;
906         }
907     }
908     return ScriptAny(flattened);
909 }
910 
911 private ScriptAny native_Array_forEach(Environment env, ScriptAny* thisObj,
912                                        ScriptAny[] args, ref NativeFunctionError nfe)
913 {
914     if(thisObj.type != ScriptAny.Type.ARRAY)
915         return ScriptAny.UNDEFINED;
916     auto arr = thisObj.toValue!ScriptArray.array;
917     if(args.length < 1)
918         return ScriptAny.UNDEFINED;
919     if(args[0].type != ScriptAny.Type.FUNCTION)
920         return ScriptAny.UNDEFINED;
921     auto thisToUse = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
922     for(size_t i = 0; i < arr.length; ++i)
923     {
924         auto temp = native_Function_call(env, &args[0],
925             [thisToUse, arr[i], ScriptAny(i), *thisObj], nfe);
926         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
927             return temp;
928     }
929     return ScriptAny.UNDEFINED;
930 }
931 
932 /**
933  * Creates an Array from any iterable
934  */
935 ScriptAny native_Array_s_from(Environment env, ScriptAny* thisObj,
936                                       ScriptAny[] args, ref NativeFunctionError nfe)
937 {
938     if(args.length < 1)
939         return ScriptAny(cast(ScriptAny[])[]);
940     ScriptAny func = args.length > 1 ? args[1] : ScriptAny.UNDEFINED;
941     auto thisToUse = args.length > 2 ? args[2] : getLocalThis(env, func);
942     
943     ScriptAny[] result;
944     if(args[0].type == ScriptAny.Type.ARRAY)
945     {
946         auto arr = args[0].toValue!ScriptArray.array;
947         for(size_t i = 0; i < arr.length; ++i)
948         {
949             if(func.type == ScriptAny.Type.FUNCTION)
950             {
951                 auto temp = native_Function_call(env, &func, [thisToUse, arr[i], ScriptAny(i), args[0]], nfe);
952                 if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
953                     return temp;
954                 result ~= temp;
955             }
956             else
957             {
958                 // args[0] is already an array and there is no callback so there is nothing else to do
959                 // and if the array is empty return results=[] will be hit.
960                 return args[0];
961             }
962         }
963     }
964     else if(args[0].type == ScriptAny.Type.STRING)
965     {
966         size_t index = 0;
967 
968         foreach(dchar ch ; args[0].toString())
969         {
970             if(func.type == ScriptAny.Type.FUNCTION)
971             {
972                 auto temp = native_Function_call(env, &func, 
973                     [thisToUse, ScriptAny([ch]), ScriptAny(index), args[0]], nfe);
974                 if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
975                     return temp;
976                 result ~= temp;
977             }
978             else
979             {
980                 result ~= ScriptAny([ch]);
981             }
982             ++index;
983         }       
984     }
985     else if(args[0].isNativeObjectType!AbstractArrayBuffer)
986     {
987         auto aab = args[0].toNativeObject!AbstractArrayBuffer; // @suppress(dscanner.suspicious.unmodified)
988         if(!aab.isView)
989             throw new ScriptRuntimeException("ArrayBuffer must be cast to view");
990         string HANDLE_TYPED_ARRAY(A)()
991         {
992             import std.format: format;
993             return format(q{
994             {
995                 alias E = typeof(%1$s.data[0]);
996                 auto a = cast(%1$s)aab;
997                 for(size_t i = 0; i < a.data.length; ++i)
998                 {
999                     if(func.type == ScriptAny.Type.FUNCTION)
1000                     {
1001                         auto temp = native_Function_call(env, &func,
1002                             [thisToUse, ScriptAny(a.data[i]), ScriptAny(i), args[0]], nfe);
1003                         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
1004                             return temp;
1005                         result ~= temp;
1006                     }
1007                     else 
1008                     {
1009                         result ~= ScriptAny(a.data[i]);
1010                     }
1011                 }
1012             }
1013             }, A.stringof);
1014         }
1015         final switch(aab.type)
1016         {
1017         case AbstractArrayBuffer.Type.ARRAY_BUFFER:
1018             break; // already handled
1019         case AbstractArrayBuffer.Type.INT8_ARRAY:
1020             mixin(HANDLE_TYPED_ARRAY!Int8Array);
1021             break;
1022         case AbstractArrayBuffer.Type.UINT8_ARRAY:
1023             mixin(HANDLE_TYPED_ARRAY!Uint8Array);
1024             break;
1025         case AbstractArrayBuffer.Type.INT16_ARRAY:
1026             mixin(HANDLE_TYPED_ARRAY!Int16Array);
1027             break;
1028         case AbstractArrayBuffer.Type.UINT16_ARRAY:
1029             mixin(HANDLE_TYPED_ARRAY!Uint16Array);
1030             break;
1031         case AbstractArrayBuffer.Type.INT32_ARRAY:
1032             mixin(HANDLE_TYPED_ARRAY!Int32Array);
1033             break;
1034         case AbstractArrayBuffer.Type.UINT32_ARRAY:
1035             mixin(HANDLE_TYPED_ARRAY!Uint32Array);
1036             break;
1037         case AbstractArrayBuffer.Type.FLOAT32_ARRAY:
1038             mixin(HANDLE_TYPED_ARRAY!Float32Array);
1039             break;
1040         case AbstractArrayBuffer.Type.FLOAT64_ARRAY:
1041             mixin(HANDLE_TYPED_ARRAY!Float64Array);
1042             break;
1043         case AbstractArrayBuffer.Type.BIGINT64_ARRAY:
1044             mixin(HANDLE_TYPED_ARRAY!BigInt64Array);
1045             break;
1046         case AbstractArrayBuffer.Type.BIGUINT64_ARRAY:
1047             mixin(HANDLE_TYPED_ARRAY!BigUint64Array);
1048             break;
1049         }
1050     }
1051     else if(args[0].isNativeObjectType!ScriptGenerator)
1052     {
1053         auto nextIteration = native_Generator_next(env, &args[0], [], nfe).toValue!ScriptObject;
1054         size_t counter = 0;
1055         while(!nextIteration["done"])
1056         {
1057             auto value = nextIteration["value"];
1058             if(func.type == ScriptAny.Type.FUNCTION)
1059             {
1060                 auto temp = native_Function_call(env, &func, [thisToUse, value, ScriptAny(counter), args[0]], nfe);
1061                 if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
1062                     return temp;
1063                 result ~= temp;
1064             }
1065             else
1066             {
1067                 result ~= value;
1068             }
1069             ++counter;
1070             nextIteration = native_Generator_next(env, &args[0], [], nfe).toValue!ScriptObject;
1071         }
1072     }
1073 
1074     return ScriptAny(result);
1075 }
1076 
1077 private ScriptAny native_Array_includes(Environment env, ScriptAny* thisObj,
1078                                         ScriptAny[] args, ref NativeFunctionError nfe)
1079 {
1080     if(thisObj.type != ScriptAny.Type.ARRAY)
1081         return ScriptAny(false);
1082     auto arr = thisObj.toValue!ScriptArray.array;
1083     if(args.length < 1)
1084         return ScriptAny(false);
1085     long indexToStart = args.length > 1 ? args[1].toValue!long : 0;
1086     if(indexToStart < 0)
1087         indexToStart = arr.length + indexToStart;
1088     if(indexToStart < 0 || indexToStart >= arr.length)
1089         indexToStart = arr.length;
1090     for(size_t i = indexToStart; i < arr.length; ++i)
1091         if(args[0].strictEquals(arr[i]))
1092             return ScriptAny(true);
1093     return ScriptAny(false);
1094 }
1095 
1096 
1097 private ScriptAny native_Array_indexOf(Environment env, ScriptAny* thisObj,
1098                                         ScriptAny[] args, ref NativeFunctionError nfe)
1099 {
1100     if(thisObj.type != ScriptAny.Type.ARRAY)
1101         return ScriptAny.UNDEFINED;
1102     auto arr = thisObj.toValue!ScriptArray.array;
1103     if(args.length < 1)
1104         return ScriptAny(-1);
1105     long indexToStart = args.length > 1 ? args[1].toValue!long : 0;
1106     if(indexToStart < 0) indexToStart += arr.length;
1107     if(indexToStart < 0 || indexToStart >= arr.length)
1108         indexToStart = arr.length;
1109     for(size_t i = indexToStart; i < arr.length; ++i)
1110         if(args[0].strictEquals(arr[i]))
1111             return ScriptAny(i);
1112     return ScriptAny(-1);
1113 }
1114 
1115 private ScriptAny native_Array_s_isArray(Environment env, ScriptAny* thisObj,
1116                                          ScriptAny[] args, ref NativeFunctionError nfe)
1117 {
1118     if(args.length < 1)
1119         return ScriptAny(false);
1120     return ScriptAny(args[0].type == ScriptAny.Type.ARRAY);
1121 }
1122 
1123 private ScriptAny native_Array_join(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe)
1124 {
1125     if(thisObj.type != ScriptAny.Type.ARRAY)
1126         return ScriptAny.UNDEFINED;
1127     auto join = ",";
1128     if(args.length > 0)
1129         join = args[0].toString();
1130     auto arr = thisObj.toValue!(string[]);
1131     string result = "";
1132     for(size_t i = 0; i < arr.length; ++i)
1133     {
1134         result ~= arr[i];
1135         if(i < arr.length - 1)
1136             result ~= join;
1137     }
1138     return ScriptAny(result);
1139 }
1140 
1141 private ScriptAny native_Array_keys(Environment env, ScriptAny* thisObj,
1142                                     ScriptAny[] args, ref NativeFunctionError nfe)
1143 {
1144     import std.concurrency: yield;
1145 
1146     if(thisObj.type != ScriptAny.Type.ARRAY)
1147         return ScriptAny.UNDEFINED;
1148     auto genFunc = new ScriptFunction("Iterator", 
1149         delegate ScriptAny(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe)
1150         {
1151             auto arr = args[0].toValue!(ScriptAny[]);
1152             foreach(key, value ; arr)
1153                 yield!ScriptAny(ScriptAny(key));
1154             return ScriptAny.UNDEFINED;
1155         }
1156     );
1157     auto generator = new ScriptGenerator(env, genFunc, [ *thisObj ]);
1158     auto iterator = new ScriptObject("Iterator", getGeneratorPrototype, generator);
1159     return ScriptAny(iterator);
1160 }
1161 
1162 private ScriptAny native_Array_lastIndexOf(Environment env, ScriptAny* thisObj,
1163                                            ScriptAny[] args, ref NativeFunctionError nfe)
1164 {
1165     if(thisObj.type != ScriptAny.Type.ARRAY)
1166         return ScriptAny.UNDEFINED;
1167     auto arr = thisObj.toValue!ScriptArray.array;
1168     if(args.length < 1)
1169         return ScriptAny(-1);
1170     long indexToStart = args.length > 1 ? args[1].toValue!long : arr.length - 1;
1171     if(indexToStart < 0) indexToStart += arr.length;
1172     if(indexToStart < 0 || indexToStart >= arr.length)
1173         indexToStart = arr.length - 1;
1174     if(arr.length == 0)
1175         return ScriptAny(-1);
1176     for(long i = indexToStart; i >= 0; --i)
1177     {
1178         if(args[0].strictEquals(arr[i]))
1179             return ScriptAny(i);
1180     }
1181     return ScriptAny(-1);    
1182 }
1183 
1184 private ScriptAny native_Array_p_length(Environment env, ScriptAny* thisObj,
1185                                         ScriptAny[] args, ref NativeFunctionError nfe)
1186 {
1187     if(thisObj.type != ScriptAny.Type.ARRAY)
1188         return ScriptAny.UNDEFINED;
1189     if(args.length >= 1)
1190     {
1191         auto actualArray = thisObj.toValue!ScriptArray;
1192         immutable length = args[0].toValue!long;
1193         actualArray.array.length = length;
1194         return ScriptAny(actualArray.array.length);
1195     }
1196     else
1197     {
1198         auto arr = thisObj.toValue!(ScriptAny[]);
1199         return ScriptAny(arr.length);
1200     }
1201 }
1202 
1203 private ScriptAny native_Array_map(Environment env, ScriptAny* thisObj,
1204                                    ScriptAny[] args, ref NativeFunctionError nfe)
1205 {
1206     if(thisObj.type != ScriptAny.Type.ARRAY)
1207         return ScriptAny.UNDEFINED;
1208     auto arr = thisObj.toValue!ScriptArray.array;
1209     if(args.length < 1)
1210         return *thisObj;
1211     if(args[0].type != ScriptAny.Type.FUNCTION)
1212         return *thisObj;
1213     ScriptAny thisToUse = args.length > 1 ? args[1] : ScriptAny.UNDEFINED;
1214     ScriptAny[] result;
1215     for(size_t i = 0; i < arr.length; ++i)
1216     {
1217         auto temp = native_Function_call(env, &args[0], 
1218             [thisToUse, arr[i], ScriptAny(i), *thisObj], nfe);
1219         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
1220             return temp;
1221         result ~= temp;
1222     }
1223     return ScriptAny(result);
1224 }
1225 
1226 private ScriptAny native_Array_s_of(Environment env, ScriptAny* thisObj,
1227                                     ScriptAny[] args, ref NativeFunctionError nfe)
1228 {
1229     ScriptAny[] results;
1230     foreach(arg ; args)
1231         results ~= arg;
1232     return ScriptAny(results);
1233 }
1234 
1235 private ScriptAny native_Array_push(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe)
1236 {
1237     if(thisObj.type != ScriptAny.Type.ARRAY)
1238         return ScriptAny.UNDEFINED;
1239     if(args.length < 0)
1240         return ScriptAny.UNDEFINED;
1241     auto arr = thisObj.toValue!ScriptArray;
1242     arr.array ~= args[0];
1243     return ScriptAny(arr.array.length);
1244 }
1245 
1246 private ScriptAny native_Array_pop(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe)
1247 {
1248     if(thisObj.type != ScriptAny.Type.ARRAY)
1249         return ScriptAny.UNDEFINED;
1250     auto arr = thisObj.toValue!ScriptArray;
1251     if(arr.array.length < 1)
1252         return ScriptAny.UNDEFINED;
1253     auto result = arr.array[$-1];
1254     arr.array = arr.array[0..$-1];
1255     return result;
1256 }
1257 
1258 private ScriptAny native_Array_reduce(Environment env, ScriptAny* thisObj,
1259                                       ScriptAny[] args, ref NativeFunctionError nfe)
1260 {
1261     if(thisObj.type != ScriptAny.Type.ARRAY)
1262         return ScriptAny.UNDEFINED;
1263     auto arr = thisObj.toValue!ScriptArray.array;
1264     if(args.length < 0 || args[0].type != ScriptAny.Type.FUNCTION)
1265         return ScriptAny.UNDEFINED;
1266     ScriptAny accumulator = args.length > 1 ? args[1] : (arr.length > 0? arr[0] : ScriptAny.UNDEFINED);
1267     immutable start = accumulator == ScriptAny.UNDEFINED ? 0 : 1;
1268     if(arr.length == 0 && args.length < 2)
1269         throw new ScriptRuntimeException("Reduce with no accumulator may not be called on empty array");
1270     for(size_t i = start; i < arr.length; ++i)
1271     {
1272         accumulator = native_Function_call(env, &args[0], 
1273             [getLocalThis(env, args[0]), accumulator, arr[i], ScriptAny(i), *thisObj], nfe);
1274         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
1275             return accumulator;
1276     }
1277     return accumulator;
1278 }
1279 
1280 private ScriptAny native_Array_reduceRight(Environment env, ScriptAny* thisObj,
1281                                       ScriptAny[] args, ref NativeFunctionError nfe)
1282 {
1283     if(thisObj.type != ScriptAny.Type.ARRAY)
1284         return ScriptAny.UNDEFINED;
1285     auto arr = thisObj.toValue!ScriptArray.array;
1286     if(args.length < 0 || args[0].type != ScriptAny.Type.FUNCTION)
1287         return ScriptAny.UNDEFINED;
1288     ScriptAny accumulator = args.length > 1 ? args[1] : (arr.length > 0? arr[arr.length-1] : ScriptAny.UNDEFINED);
1289     immutable long start = accumulator == ScriptAny.UNDEFINED ? arr.length : cast(long)arr.length - 1;
1290     if(arr.length == 0 && args.length < 2)
1291         throw new ScriptRuntimeException("Reduce right with no accumulator may not be called on empty array");
1292     if(start < 0)
1293         return ScriptAny.UNDEFINED;
1294     for(long i = start; i > 0; --i)
1295     {
1296         accumulator = native_Function_call(env, &args[0], 
1297             [getLocalThis(env, args[0]), accumulator, arr[i-1], ScriptAny(i-1), *thisObj], nfe);
1298         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
1299             return accumulator;
1300     }
1301     return accumulator;
1302 }
1303 
1304 private ScriptAny native_Array_reverse(Environment env, ScriptAny* thisObj,
1305                                        ScriptAny[] args, ref NativeFunctionError nfe)
1306 {
1307     import std.algorithm.mutation: reverse;
1308     if(thisObj.type != ScriptAny.Type.ARRAY)
1309         return ScriptAny.UNDEFINED;
1310     auto arr = thisObj.toValue!ScriptArray;
1311     reverse(arr.array);
1312     return *thisObj;
1313 }
1314 
1315 private ScriptAny native_Array_shift(Environment env, ScriptAny* thisObj,
1316                                      ScriptAny[] args, ref NativeFunctionError nfe)
1317 {
1318     if(thisObj.type != ScriptAny.Type.ARRAY)
1319         return ScriptAny.UNDEFINED;
1320     auto arr = thisObj.toValue!ScriptArray;
1321     if(arr.array.length < 1)
1322         return ScriptAny.UNDEFINED;
1323     auto removed = arr.array[0];
1324     arr.array = arr.array[1..$];
1325     return removed;
1326 }
1327 
1328 private ScriptAny native_Array_slice(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe)
1329 {
1330     if(thisObj.type != ScriptAny.Type.ARRAY)
1331         return ScriptAny.UNDEFINED;
1332     auto array = thisObj.toValue!(ScriptAny[]);
1333     long start = args.length > 0 ? args[0].toValue!long : 0;
1334     long end = args.length > 1 ? args[1].toValue!long : array.length;
1335     if(start < 0)
1336         start = array.length + start;
1337     if(end < 0)
1338         end = array.length + end;
1339     if(start < 0 || start >= array.length)
1340         start = 0;
1341     if(end < 0 || end > array.length)
1342         end = array.length;
1343     if(end < start)
1344     {
1345         immutable temp = end;
1346         end = start;
1347         start = temp;
1348     }
1349     return ScriptAny(array[start .. end]);
1350 }
1351 
1352 private ScriptAny native_Array_some(Environment env, ScriptAny* thisObj, 
1353                                     ScriptAny[] args, ref NativeFunctionError nfe)
1354 {
1355     if(thisObj.type != ScriptAny.Type.ARRAY)
1356         return ScriptAny.UNDEFINED;
1357     if(args.length < 1 || args[0].type != ScriptAny.Type.FUNCTION)
1358         return ScriptAny.UNDEFINED;
1359     auto arr = thisObj.toValue!ScriptArray.array;
1360     ScriptAny thisToUse = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
1361     for(size_t i = 0; i < arr.length; ++i)
1362     {
1363         auto temp = native_Function_call(env, &args[0], 
1364             [thisToUse, arr[i], ScriptAny(i), *thisObj], nfe);
1365         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR || temp)
1366             return temp;
1367     }
1368     return ScriptAny(false);
1369 }
1370 
1371 private ScriptAny native_Array_sort(Environment env, ScriptAny* thisObj,
1372                                     ScriptAny[] args, ref NativeFunctionError nfe)
1373 {
1374     import std.algorithm: sort;
1375     if(thisObj.type != ScriptAny.Type.ARRAY)
1376         return ScriptAny.UNDEFINED;
1377     auto arr = thisObj.toValue!ScriptArray;
1378     if(arr.array.length <= 1)
1379         return *thisObj;
1380     if(args.length < 1 || args[0].type != ScriptAny.Type.FUNCTION)
1381     {
1382         sort(arr.array);
1383     }
1384     else
1385     {
1386         // use bubble sort
1387         for(size_t i = 0; i < arr.length-1; ++i)
1388         {
1389             for(size_t j = 0; j < arr.length - i - 1; ++j)
1390             {
1391                 auto temp = native_Function_call(env, &args[0], 
1392                     [getLocalThis(env, args[0]), arr.array[j], arr.array[j+1]], nfe);
1393                 if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
1394                     return temp;
1395                 if(temp.toValue!int > 0)
1396                 {
1397                     auto swap = arr.array[j+1]; // @suppress(dscanner.suspicious.unmodified)
1398                     arr.array[j+1] = arr.array[j];
1399                     arr.array[j] = swap;
1400                 }
1401             }
1402         }
1403     }
1404     return *thisObj;
1405 }
1406 
1407 private ScriptAny native_Array_splice(Environment env, ScriptAny* thisObj, 
1408                                       ScriptAny[] args, ref NativeFunctionError nfe)
1409 {
1410     import std.algorithm: min;
1411     if(thisObj.type != ScriptAny.Type.ARRAY)
1412         return ScriptAny.UNDEFINED;
1413     auto arr = thisObj.toValue!ScriptArray;
1414     if(args.length < 1)
1415         return ScriptAny.UNDEFINED;
1416     immutable start = min(args[0].toValue!size_t, arr.array.length - 1);
1417     if(start >= arr.array.length)
1418         return ScriptAny.UNDEFINED;
1419     immutable deleteCount = args.length > 1 ? min(args[1].toValue!size_t, arr.array.length) : arr.array.length - start;
1420     ScriptAny[] removed = [];
1421     if(args.length > 2)
1422         args = args[2 .. $];
1423     else
1424         args = [];
1425     // copy elements up to start
1426     ScriptAny[] result = arr.array[0 .. start];
1427     // add new elements supplied as args
1428     result ~= args;
1429     // copy removed items to removed array
1430     removed ~= arr.array[start .. start+deleteCount];
1431     // add those after start plus delete count
1432     result ~= arr.array[start+deleteCount .. $];
1433     // set the original array
1434     arr.array = result;
1435     // return the removed items
1436     return ScriptAny(removed);
1437 }
1438 
1439 private ScriptAny native_Array_unshift(Environment env, ScriptAny* thisObj,
1440                                        ScriptAny[] args, ref NativeFunctionError nfe)
1441 {
1442     if(thisObj.type != ScriptAny.Type.ARRAY)
1443         return ScriptAny.UNDEFINED;
1444     auto arr = thisObj.toValue!ScriptArray;
1445     arr.array = args ~ arr.array;
1446     return ScriptAny(arr.length);
1447 }
1448 
1449 private ScriptAny native_Array_values(Environment env, ScriptAny* thisObj,
1450                                       ScriptAny[] args, ref NativeFunctionError nfe)
1451 {
1452     import std.concurrency: yield;
1453     if(thisObj.type != ScriptAny.Type.ARRAY)
1454         return ScriptAny.UNDEFINED;
1455     auto genFunc = new ScriptFunction("Iterator",
1456         delegate ScriptAny(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe)
1457         {
1458             auto arr = args[0].toValue!(ScriptAny[]);
1459             foreach(value ; arr)
1460                 yield!ScriptAny( value );
1461             return ScriptAny.UNDEFINED;
1462         }
1463     );
1464     auto generator = new ScriptGenerator(env, genFunc, [*thisObj]);
1465     auto iterator = new ScriptObject("Iterator", getGeneratorPrototype, generator);
1466     return ScriptAny(iterator);
1467 }
1468 
1469 //
1470 // Function methods ///////////////////////////////////////////////////////////
1471 //
1472 
1473 private ScriptAny native_Function_apply(Environment env, ScriptAny* thisIsFn, ScriptAny[] args,
1474                                         ref NativeFunctionError nfe)
1475 {
1476     import mildew.exceptions: ScriptRuntimeException;
1477     // minimum args is 2 because first arg is the this to use and the second is an array
1478     if(args.length < 2)
1479     {
1480         nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS;
1481         return ScriptAny.UNDEFINED;
1482     }
1483     // get the function
1484     if(thisIsFn.type != ScriptAny.Type.FUNCTION)
1485     {
1486         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
1487         return ScriptAny.UNDEFINED;
1488     }
1489     auto fn = thisIsFn.toValue!ScriptFunction;
1490     // set up the "this" to use
1491     auto thisToUse = args[0];
1492     // set up the arg array. TODO handle all iterables?
1493     if(args[1].type != ScriptAny.Type.ARRAY)
1494     {
1495         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
1496         return ScriptAny.UNDEFINED;
1497     }
1498     ScriptAny[] argList = args[1].toValue!(ScriptAny[]);
1499     if(fn.isGenerator)
1500     {
1501         auto obj = new ScriptObject("Generator", getGeneratorPrototype, new ScriptGenerator(
1502             env, fn, argList, thisToUse));
1503         return ScriptAny(obj);
1504     }
1505     else
1506     {
1507         auto interpreter = env.interpreter;
1508         if(interpreter is null)
1509         {
1510             nfe = NativeFunctionError.RETURN_VALUE_IS_EXCEPTION;
1511             return ScriptAny("Interpreter was improperly created without global environment");
1512         }
1513         return interpreter.vm.runFunction(fn, thisToUse, argList);
1514     }
1515 }
1516 
1517 private ScriptAny native_Function_bind(Environment env, ScriptAny* thisObj,
1518                                        ScriptAny[] args, ref NativeFunctionError nfe)
1519 {
1520     if(thisObj.type != ScriptAny.Type.FUNCTION)
1521         return ScriptAny.UNDEFINED;
1522     auto fn = thisObj.toValue!ScriptFunction.copy(env);
1523     ScriptAny newBinding = args.length > 0 ? args[0] : ScriptAny.UNDEFINED;
1524     fn.bind(newBinding);
1525     return ScriptAny(fn);
1526 }
1527 
1528 /**
1529  * This function provides a way for Mildew functions to be called with arbitrary "this"
1530  * objects. This function is public so that there is a common interface for calling ScriptFunctions
1531  * without worrying about the underlying details.
1532  *
1533  * Params:
1534  *  env = Since this function is to be called from other native functions, this should be the Environment object
1535  *        received. The underlying function handlers will handle the closure data of ScriptFunctions.
1536  *  thisIfFn = This should be a ScriptAny pointer of the ScriptFunction to be called.
1537  *  args = An array of arguments to call the function with, but the first element must be the "this" object to use.
1538  *  nfe = Since this function is to be called from native ScriptFunction implementations, this should be the same
1539  *        NativeFunctionError reference. This must always be checked after using native_Function_call directly.
1540  *
1541  * Returns:
1542  *  The return value of calling the ScriptFunction.
1543  */
1544 ScriptAny native_Function_call(Environment env, ScriptAny* thisIsFn, ScriptAny[] args, 
1545                                        ref NativeFunctionError nfe)
1546 {
1547     import mildew.exceptions: ScriptRuntimeException;
1548     // minimum args is 1 because first arg is the this to use
1549     if(args.length < 1)
1550     {
1551         nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS;
1552         return ScriptAny.UNDEFINED;
1553     }
1554     // get the function
1555     if(thisIsFn.type != ScriptAny.Type.FUNCTION)
1556     {
1557         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
1558         return ScriptAny.UNDEFINED;
1559     }
1560     auto fn = thisIsFn.toValue!ScriptFunction;
1561     // set up the "this" to use
1562     auto thisToUse = args[0];
1563     // now send the remainder of the args to a called function with this setup
1564     args = args[1..$];
1565     if(fn.isGenerator)
1566     {
1567         auto obj = new ScriptObject("Generator", getGeneratorPrototype, new ScriptGenerator(
1568             env, fn, args, thisToUse));
1569         return ScriptAny(obj);
1570     }
1571     else
1572     {
1573         auto interpreter = env.interpreter;
1574         if(interpreter is null)
1575         {
1576             nfe = NativeFunctionError.RETURN_VALUE_IS_EXCEPTION;
1577             return ScriptAny("Interpreter was improperly created without global environment");
1578         }
1579         return interpreter.vm.runFunction(fn, thisToUse, args);
1580     }
1581 }
1582 
1583 private ScriptAny native_Function_p_isGenerator(Environment env, ScriptAny* thisObj,
1584                                                 ScriptAny[] args, ref NativeFunctionError nfe)
1585 {
1586     if(thisObj.type != ScriptAny.Type.FUNCTION)
1587         return ScriptAny(false);
1588     auto func = thisObj.toValue!ScriptFunction;
1589     return ScriptAny(func.isGenerator);
1590 }
1591 
1592 private ScriptAny native_Function_p_isNative(Environment env, ScriptAny* thisObj,
1593                                              ScriptAny[] args, ref NativeFunctionError nfe)
1594 {
1595     if(thisObj.type != ScriptAny.Type.FUNCTION)
1596         return ScriptAny(false);
1597     auto func = thisObj.toValue!ScriptFunction;
1598     return ScriptAny(func.type == ScriptFunction.Type.NATIVE_DELEGATE 
1599         || func.type == ScriptFunction.Type.NATIVE_FUNCTION);
1600 }
1601 
1602 private ScriptAny native_Function_p_length(Environment env, ScriptAny* thisObj,
1603                                            ScriptAny[] args, ref NativeFunctionError nfe)
1604 {
1605     if(thisObj.type != ScriptAny.Type.FUNCTION)
1606         return ScriptAny(0);
1607     auto func = thisObj.toValue!ScriptFunction;
1608     return ScriptAny(func.argNames.length);
1609 }
1610 
1611 private ScriptAny native_Function_p_name(Environment env, ScriptAny* thisObj,
1612                                          ScriptAny[] args, ref NativeFunctionError nfe)
1613 {
1614     if(thisObj.type != ScriptAny.Type.FUNCTION)
1615         return ScriptAny("");
1616     auto func = thisObj.toValue!ScriptFunction;
1617     return ScriptAny(func.functionName);
1618 }
1619 
1620 //
1621 // String methods /////////////////////////////////////////////////////////////  
1622 //
1623 
1624 /// Creates a string by converting arguments to strings and concatenating them
1625 private ScriptAny native_String_ctor(Environment env, ScriptAny* thisObj,
1626                                      ScriptAny[] args, ref NativeFunctionError nfe)
1627 {
1628     auto str = "";
1629     foreach(arg ; args)
1630     {
1631         str ~= arg.toString();
1632     }
1633     *thisObj = ScriptAny(str);
1634     return ScriptAny.UNDEFINED;
1635 }
1636 
1637 private ScriptAny native_String_charAt(Environment env, ScriptAny* thisObj,
1638                                        ScriptAny[] args, ref NativeFunctionError nfe)
1639 {
1640     import std.utf: UTFException;
1641 
1642     if(thisObj.type != ScriptAny.Type.STRING)
1643         return ScriptAny.UNDEFINED;
1644 
1645     auto ss = thisObj.toString();
1646     immutable size_t index = args.length > 0 ? args[0].toValue!size_t : 0;
1647 
1648     if(index >= ss.length)
1649         return ScriptAny("");
1650 
1651     try
1652     {
1653         immutable char ch = ss[index];
1654         return ScriptAny([ch]);
1655     }
1656     catch(UTFException ex)
1657     {
1658         return ScriptAny("");
1659     }
1660 }
1661 
1662 private ScriptAny native_String_charCodeAt(Environment c, ScriptAny* thisObj,
1663                                        ScriptAny[] args, ref NativeFunctionError nfe)
1664 {
1665     if(thisObj.type != ScriptAny.Type.STRING)
1666         return ScriptAny.UNDEFINED;
1667     if(args.length < 1)
1668         return ScriptAny.UNDEFINED;
1669 
1670     auto ss = thisObj.toString();
1671     immutable size_t index = args.length > 0 ? args[0].toValue!size_t : 0;
1672 
1673     if(index >= ss.length)
1674         return ScriptAny(0);
1675 
1676     return ScriptAny(cast(ubyte)ss[index]);
1677 }
1678 
1679 private ScriptAny native_String_codePointAt(Environment env, ScriptAny* thisObj,
1680                                             ScriptAny[] args, ref NativeFunctionError nfe)
1681 {
1682     if(thisObj.type != ScriptAny.Type.STRING)
1683         return ScriptAny.UNDEFINED;
1684 
1685     auto str = thisObj.toString();
1686     immutable size_t index = args.length >= 1 ? args[0].toValue!size_t: 0;
1687     size_t counter = 0;
1688     foreach(dchar dch ; str)
1689     {
1690         if(counter == index)
1691             return ScriptAny(cast(uint)dch);
1692         ++counter;
1693     }
1694     return ScriptAny.UNDEFINED;
1695 }
1696 
1697 private ScriptAny native_String_concat(Environment env, ScriptAny* thisObj,
1698                                        ScriptAny[] args, ref NativeFunctionError nfe)
1699 {
1700     if(thisObj.type != ScriptAny.Type.STRING)
1701         return ScriptAny.UNDEFINED;
1702     auto str = thisObj.toString();
1703     foreach(arg ; args)
1704     {
1705         str ~= arg.toString();
1706     }
1707     return ScriptAny(str);
1708 }
1709 
1710 private ScriptAny native_String_endsWith(Environment env, ScriptAny* thisObj,
1711                                          ScriptAny[] args, ref NativeFunctionError nfe)
1712 {
1713     if(thisObj.type != ScriptAny.Type.STRING)
1714         return ScriptAny(false);
1715     auto str = thisObj.toString();
1716     if(args.length < 1)
1717         return ScriptAny(true);
1718     auto testStr = args[0].toString();
1719     size_t limit = args.length > 1 ? args[1].toValue!size_t : str.length;
1720     if(limit > str.length)
1721         limit = str.length;
1722     str = str[0..limit];
1723     if(testStr.length > str.length)
1724         return ScriptAny(false);
1725     return ScriptAny(str[$-testStr.length .. $] == testStr);
1726 }
1727 
1728 private ScriptAny native_String_s_fromCharCode(Environment env, ScriptAny* thisObj,
1729                                                ScriptAny[] args, ref NativeFunctionError nfe)
1730 {
1731     import std.utf: UTFException;
1732     auto result = "";
1733     foreach(arg ; args)
1734     {
1735         try 
1736         {
1737             result ~= cast(char)(arg.toValue!uint % 256);
1738         }
1739         catch(UTFException ex)
1740         {
1741             return ScriptAny.UNDEFINED;
1742         }
1743     }
1744     return ScriptAny(result);
1745 }
1746 
1747 private ScriptAny native_String_s_fromCodePoint(Environment env, ScriptAny* thisObj,
1748                                                 ScriptAny[] args, ref NativeFunctionError nfe)
1749 {
1750     import std.utf: UTFException;
1751     dstring result = "";
1752     foreach(arg ; args)
1753     {
1754         try 
1755         {
1756             result ~= cast(dchar)(arg.toValue!uint);
1757         }
1758         catch(UTFException ex)
1759         {
1760             return ScriptAny.UNDEFINED;
1761         }
1762     }
1763     return ScriptAny(result);
1764 }
1765 
1766 private ScriptAny native_String_includes(Environment env, ScriptAny* thisObj,
1767                                          ScriptAny[] args, ref NativeFunctionError nfe)
1768 {
1769     import std..string: indexOf;
1770     if(thisObj.type != ScriptAny.Type.STRING)
1771         return ScriptAny(false);
1772     auto str = thisObj.toString();
1773     if(args.length < 1)
1774         return ScriptAny(true);
1775     auto search = args[0].toString();
1776     return ScriptAny(str.indexOf(search) != -1);
1777 }
1778 
1779 private ScriptAny native_String_indexOf(Environment env, ScriptAny* thisObj,
1780                                         ScriptAny[] args, ref NativeFunctionError nfe)
1781 {
1782     import std..string: indexOf;
1783     if(thisObj.type != ScriptAny.Type.STRING)
1784         return ScriptAny(-1);
1785     auto str = thisObj.toString();
1786     if(args.length < 1)
1787         return ScriptAny(0);
1788     auto searchText = args[0].toString();
1789     return ScriptAny(str.indexOf(searchText));
1790 }
1791 
1792 private ScriptAny native_String_lastIndexOf(Environment env, ScriptAny* thisObj,
1793                                             ScriptAny[] args, ref NativeFunctionError nfe)
1794 {
1795     import std..string: lastIndexOf;
1796     if(thisObj.type != ScriptAny.Type.STRING)
1797         return ScriptAny(-1);
1798     auto str = thisObj.toString();
1799     if(args.length < 1)
1800         return ScriptAny(0);
1801     auto searchText = args[0].toString();
1802     immutable startIdx = args.length > 1 ? args[1].toValue!long : str.length;
1803     return ScriptAny(str.lastIndexOf(searchText, startIdx));
1804 }
1805 
1806 private ScriptAny native_String_p_length(Environment env, ScriptAny* thisObj,
1807                                          ScriptAny[] args, ref NativeFunctionError nfe)
1808 {
1809     if(thisObj.type != ScriptAny.Type.STRING)
1810         return ScriptAny.UNDEFINED;
1811     auto str = thisObj.toString();
1812     return ScriptAny(str.length);
1813 }
1814 
1815 private ScriptAny native_String_match(Environment env, ScriptAny* thisObj,
1816                                       ScriptAny[] args, ref NativeFunctionError nfe)
1817 {
1818     if(thisObj.type != ScriptAny.Type.STRING || args.length < 1)
1819         return ScriptAny(null);
1820     ScriptRegExp regExp = args[0].toNativeObject!ScriptRegExp;
1821     if(regExp is null)
1822         return ScriptAny(null);
1823     return ScriptAny(regExp.match(thisObj.toString()));
1824 }
1825 
1826 private ScriptAny native_String_matchAll(Environment env, ScriptAny* thisObj,
1827                                          ScriptAny[] args, ref NativeFunctionError nfe)
1828 {
1829     import std.concurrency: yield;
1830 
1831     if(thisObj.type != ScriptAny.Type.STRING || args.length < 1)
1832         return ScriptAny.UNDEFINED;
1833     auto str = thisObj.toString();
1834     
1835     ScriptRegExp regExp = args[0].toNativeObject!ScriptRegExp;
1836     if(regExp is null)
1837     {
1838         try 
1839         {
1840             regExp = new ScriptRegExp(args[0].toString());
1841         }
1842         catch(Exception)
1843         {
1844             return ScriptAny.UNDEFINED;
1845         }
1846     }
1847     
1848     ScriptAny func(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe){
1849         foreach(match ; regExp.matchAll(str))
1850             yield!ScriptAny(ScriptAny(match.hit));
1851         ScriptAny undef;
1852         return undef;
1853     }
1854 
1855     auto generator = new ScriptGenerator(env, new ScriptFunction("Iterator", &func), []);
1856     auto result = new ScriptObject("Iterator", getGeneratorPrototype, generator);
1857     return ScriptAny(result);
1858 }
1859 
1860 private ScriptAny native_String_normalize(Environment env, ScriptAny* thisObj,
1861                                           ScriptAny[] args, ref NativeFunctionError nfe)
1862 {
1863     import std.uni: normalize, NFC, NFD, NFKC, NFKD;
1864     if(thisObj.type != ScriptAny.Type.STRING)
1865         return ScriptAny.UNDEFINED;
1866     if(args.length < 1)
1867         return ScriptAny(normalize(thisObj.toString()));
1868     auto form = args[0].toString(); // @suppress(dscanner.suspicious.unmodified)
1869     if(form == "NFD")
1870         return ScriptAny(normalize!NFD(thisObj.toString()));
1871     else if(form == "NFKC")
1872         return ScriptAny(normalize!NFKC(thisObj.toString()));
1873     else if(form == "NFKD")
1874         return ScriptAny(normalize!NFKD(thisObj.toString()));
1875     else
1876         return ScriptAny(normalize!NFC(thisObj.toString())); 
1877 }
1878 
1879 private ScriptAny native_String_padEnd(Environment env, ScriptAny* thisObj,
1880                                        ScriptAny[] args, ref NativeFunctionError nfe)
1881 {
1882     if(thisObj.type != ScriptAny.Type.STRING)
1883         return ScriptAny.UNDEFINED;
1884     auto str = thisObj.toString();
1885     if(args.length < 1)
1886         return *thisObj;
1887     immutable numPadding = args[0].toValue!long;
1888     auto padding = args.length > 1 ? args[1].toString(): " ";
1889     if(str.length > numPadding)
1890         return *thisObj;
1891     if(padding.length == 0)
1892         return *thisObj;
1893     immutable amountToAdd = (numPadding - str.length) / padding.length;
1894     for(auto i = 0; i < amountToAdd+1; ++i)
1895         str ~= padding;
1896     return ScriptAny(str[0..numPadding]);
1897 }
1898 
1899 private ScriptAny native_String_padStart(Environment env, ScriptAny* thisObj,
1900                                          ScriptAny[] args, ref NativeFunctionError nfe)
1901 {
1902     if(thisObj.type != ScriptAny.Type.STRING)
1903         return ScriptAny.UNDEFINED;
1904     auto str = thisObj.toString();
1905     if(args.length < 1)
1906         return *thisObj;
1907     immutable numPadding = args[0].toValue!long;
1908     auto padding = args.length > 1 ? args[1].toString(): " ";
1909     if(padding.length == 0)
1910         return *thisObj;
1911     if(str.length > numPadding)
1912         return *thisObj;
1913     immutable amountToAdd = (numPadding - str.length) / padding.length;
1914     string frontString = "";
1915     for(auto i = 0; i < amountToAdd+1; ++i)
1916         frontString ~= padding;
1917     frontString = frontString[0..numPadding-str.length];
1918     return ScriptAny(frontString ~ str);
1919 }
1920 
1921 // String.raw is a Lexer directive not a method
1922 
1923 private ScriptAny native_String_repeat(Environment env, ScriptAny* thisObj,
1924                                        ScriptAny[] args, ref NativeFunctionError nfe)
1925 {
1926     if(thisObj.type != ScriptAny.Type.STRING)
1927         return ScriptAny.UNDEFINED;
1928     auto str = thisObj.toString();
1929     auto result = "";
1930     immutable size_t timesToRepeat = args.length >= 1 ? args[0].toValue!size_t: 0;
1931     for(size_t i = 0; i < timesToRepeat; ++i)
1932         result ~= str;
1933     return ScriptAny(result);
1934 }
1935 
1936 private ScriptAny native_String_replace(Environment env, ScriptAny* thisObj,
1937                                         ScriptAny[] args, ref NativeFunctionError nfe)
1938 {
1939     import std.array: replaceFirst;
1940     import std..string: indexOf;
1941 
1942     if(thisObj.type != ScriptAny.Type.STRING)
1943         return ScriptAny.UNDEFINED;
1944     if(args.length < 2)
1945         return *thisObj;
1946     
1947     auto thisString = thisObj.toString();
1948 
1949     if(args[0].isNativeObjectType!ScriptRegExp)
1950     {
1951         auto regex = args[0].toNativeObject!ScriptRegExp;
1952         if(args[1].type == ScriptAny.Type.FUNCTION)
1953         {
1954             auto match = regex.match(thisString);
1955             foreach(mat ; match)
1956             {
1957                 auto send = [getLocalThis(env, args[1]), ScriptAny(mat)];
1958                 send ~= ScriptAny(thisString.indexOf(mat));
1959                 send ~= ScriptAny(thisString);
1960                 auto replacement = native_Function_call(env, &args[1], send, nfe);
1961                 if(env.g.interpreter.vm.hasException)
1962                     return replacement;
1963                 if(nfe != NativeFunctionError.NO_ERROR)
1964                     return replacement;
1965                 thisString = replaceFirst(thisString, mat, replacement.toString());
1966             }
1967             return ScriptAny(thisString);
1968         }
1969         else
1970         {
1971             auto substr = args[1].toString();
1972             return ScriptAny(regex.replaceFirst(thisString, substr));
1973         }
1974     }
1975     else
1976     {
1977         auto substr = args[0].toString;
1978         if(args[1].type == ScriptAny.Type.FUNCTION)
1979         {
1980             immutable index = thisString.indexOf(substr);
1981             if(index == -1)
1982                 return ScriptAny(thisString);
1983             auto replacement = native_Function_call(env, &args[1], [
1984                     getLocalThis(env, args[1]),
1985                     ScriptAny(substr),
1986                     ScriptAny(index),
1987                     ScriptAny(thisString)
1988             ], nfe);
1989             if(env.g.interpreter.vm.hasException)
1990                 return replacement;
1991             if(nfe != NativeFunctionError.NO_ERROR)
1992                 return replacement;
1993             thisString = replaceFirst(thisString, substr, replacement.toString);
1994             return ScriptAny(thisString);
1995         }
1996         else
1997         {
1998             return ScriptAny(replaceFirst(thisObj.toString, substr, args[1].toString));
1999         }
2000     }
2001 }
2002 
2003 private ScriptAny native_String_replaceAll(Environment env, ScriptAny* thisObj,
2004                                         ScriptAny[] args, ref NativeFunctionError nfe)
2005 {
2006 import std.array: replace;
2007     import std..string: indexOf;
2008 
2009     if(thisObj.type != ScriptAny.Type.STRING)
2010         return ScriptAny.UNDEFINED;
2011     if(args.length < 2)
2012         return *thisObj;
2013     
2014     auto thisString = thisObj.toString();
2015 
2016     if(args[0].isNativeObjectType!ScriptRegExp)
2017     {
2018         auto regex = args[0].toNativeObject!ScriptRegExp;
2019         if(args[1].type == ScriptAny.Type.FUNCTION)
2020         {
2021             auto matches = regex.matchAll(thisString);
2022             foreach(match ; matches)
2023             {
2024                 auto send = [getLocalThis(env, args[1]), ScriptAny(match.hit)];
2025                 foreach(group ; match)
2026                     send ~= ScriptAny(group);
2027                 send ~= ScriptAny(match.pre.length);
2028                 send ~= ScriptAny(thisString);
2029                 auto replacement = native_Function_call(env, &args[1], send, nfe);
2030                 if(env.g.interpreter.vm.hasException)
2031                     return replacement;
2032                 if(nfe != NativeFunctionError.NO_ERROR)
2033                     return replacement;
2034                 thisString = replace(thisString, match.hit, replacement.toString());
2035             }
2036             return ScriptAny(thisString);
2037         }
2038         else
2039         {
2040             auto substr = args[1].toString();
2041             return ScriptAny(regex.replace(thisString, substr));
2042         }
2043     }
2044     else
2045     {
2046         auto substr = args[0].toString;
2047         if(args[1].type == ScriptAny.Type.FUNCTION)
2048         {
2049             while(true)
2050             {
2051                 immutable index = thisString.indexOf(substr);
2052                 if(index == -1)
2053                     break;
2054                 auto replacement = native_Function_call(env, &args[1], [
2055                         getLocalThis(env, args[1]),
2056                         ScriptAny(substr),
2057                         ScriptAny(index),
2058                         ScriptAny(thisString)
2059                 ], nfe);
2060                 if(env.g.interpreter.vm.hasException)
2061                     return replacement;
2062                 if(nfe != NativeFunctionError.NO_ERROR)
2063                     return replacement;
2064                 thisString = replace(thisString, substr, replacement.toString);
2065                 if(replacement.toString() == substr)
2066                     break;
2067             }
2068             return ScriptAny(thisString);
2069         }
2070         else
2071         {
2072             return ScriptAny(replace(thisObj.toString, substr, args[1].toString));
2073         }
2074     }
2075 }
2076 
2077 private ScriptAny native_String_search(Environment env, ScriptAny* thisObj,
2078                                        ScriptAny[] args, ref NativeFunctionError nfe)
2079 {
2080     if(thisObj.type != ScriptAny.Type.STRING)
2081         return ScriptAny.UNDEFINED;
2082     
2083     if(args.length < 1)
2084         return ScriptAny.UNDEFINED;
2085     
2086     ScriptRegExp regex = args[0].toNativeObject!ScriptRegExp;
2087     if(regex is null)
2088         regex = new ScriptRegExp(args[0].toString());
2089     
2090     return ScriptAny(regex.search(thisObj.toString()));
2091 }
2092 
2093 private ScriptAny native_String_slice(Environment env, ScriptAny* thisObj,
2094                                       ScriptAny[] args, ref NativeFunctionError nfe)
2095 {
2096     import std.utf : UTFException;
2097     if(thisObj.type != ScriptAny.Type.STRING)
2098         return ScriptAny.UNDEFINED;
2099     
2100     auto str = thisObj.toString();
2101 
2102     long start = args.length > 0 ? args[0].toValue!long : 0;
2103     long end = args.length > 1 ? args[1].toValue!long : str.length;
2104 
2105     if(start < 0)
2106         start = str.length + start;
2107     if(end < 0)
2108         end = str.length + end;
2109     
2110     if(start < 0 || start > str.length)
2111         start = 0;
2112     
2113     if(end < 0 || end > str.length)
2114         end = str.length;
2115 
2116     try 
2117     {
2118         str = str[start..end];
2119         return ScriptAny(str);
2120     }
2121     catch(UTFException ex)
2122     {
2123         return ScriptAny.UNDEFINED;
2124     }
2125 }
2126 
2127 private ScriptAny native_String_split(Environment env, ScriptAny* thisObj, 
2128                                       ScriptAny[] args, ref NativeFunctionError nfe)
2129 {
2130     import std.array: split;
2131     import std.conv: to;
2132     if(thisObj.type != ScriptAny.Type.STRING)
2133         return ScriptAny.UNDEFINED;
2134     auto splitter = ",";
2135     if(args.length > 0)
2136         splitter = args[0].toString();
2137     string[] splitResult;
2138     splitResult = thisObj.toString().split(splitter);
2139     return ScriptAny(splitResult);
2140 }
2141 
2142 private ScriptAny native_String_startsWith(Environment env, ScriptAny* thisObj,
2143                                            ScriptAny[] args, ref NativeFunctionError nfe)
2144 {
2145     if(thisObj.type != ScriptAny.Type.STRING)
2146         return ScriptAny(false);
2147     auto str = thisObj.toString();
2148     string substring = args.length > 0 ? args[0].toString() : "";
2149     size_t startIndex = args.length > 1 ? args[1].toValue!size_t : 0;
2150     if(startIndex > str.length)
2151         startIndex = str.length;
2152     str = str[startIndex..$];
2153     if(str.length < substring.length)
2154         return ScriptAny(false);
2155     return ScriptAny(str[0..substring.length] == substring);
2156 }
2157 
2158 private ScriptAny native_String_substring(Environment env, ScriptAny* thisObj,
2159                                           ScriptAny[] args, ref NativeFunctionError nfe)
2160 {
2161     import std.conv: to;
2162 
2163     if(thisObj.type != ScriptAny.Type.STRING)
2164         return ScriptAny.UNDEFINED;
2165     auto dstr = to!dstring(thisObj.toString());
2166     long startIndex = args.length > 0 ? args[0].toValue!long : 0;
2167     long endIndex = args.length > 1 ? args[1].toValue!long : dstr.length;
2168     if(startIndex < 0)
2169         startIndex = dstr.length + startIndex;
2170     if(endIndex < 0)
2171         endIndex = dstr.length + endIndex;
2172     if(startIndex < 0 || startIndex >= dstr.length)
2173         startIndex = 0;
2174     if(endIndex < 0 || endIndex >= dstr.length)
2175         endIndex = dstr.length;
2176     return ScriptAny(dstr[startIndex..endIndex]);
2177 }
2178 
2179 private ScriptAny native_String_toLowerCase(Environment env, ScriptAny* thisObj,
2180                                             ScriptAny[] args, ref NativeFunctionError nfe)
2181 {
2182     import std.uni : toLower;
2183     if(thisObj.type != ScriptAny.Type.STRING)
2184         return ScriptAny.UNDEFINED;
2185     return ScriptAny(toLower(thisObj.toString()));
2186 }
2187 
2188 private ScriptAny native_String_toUpperCase(Environment env, ScriptAny* thisObj,
2189                                             ScriptAny[] args, ref NativeFunctionError nfe)
2190 {
2191     import std.uni : toUpper;
2192     if(thisObj.type != ScriptAny.Type.STRING)
2193         return ScriptAny.UNDEFINED;
2194     return ScriptAny(toUpper(thisObj.toString()));
2195 }