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