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