1 /**
2 This module implements the ArrayBuffer and its associated views.
3 
4 ────────────────────────────────────────────────────────────────────────────────
5 
6 Copyright (C) 2021 pillager86.rf.gd
7 
8 This program is free software: you can redistribute it and/or modify it under 
9 the terms of the GNU General Public License as published by the Free Software 
10 Foundation, either version 3 of the License, or (at your option) any later 
11 version.
12 
13 This program is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 PARTICULAR PURPOSE.  See the GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License along with 
18 this program.  If not, see <https://www.gnu.org/licenses/>.
19 */
20 module mildew.stdlib.buffers;
21 
22 import std.format;
23 
24 import mildew.environment;
25 import mildew.exceptions;
26 import mildew.interpreter;
27 import mildew.stdlib.generator;
28 import mildew.types;
29 
30 /**
31  * Initializes the ArrayBuffer and its views library.
32  * Params:
33  *  interpreter = The Interpreter instance to load this library into
34  */
35 void initializeBuffersLibrary(Interpreter interpreter)
36 {
37     ScriptAny arrayBufferCtor = new ScriptFunction("ArrayBuffer", &native_ArrayBuffer_ctor);
38     arrayBufferCtor["isView"] = new ScriptFunction("ArrayBuffer.isView", &native_ArrayBuffer_s_isView);
39     arrayBufferCtor["prototype"] = getArrayBufferPrototype();
40     arrayBufferCtor["prototype"]["constructor"] = arrayBufferCtor;
41     interpreter.forceSetGlobal("ArrayBuffer", arrayBufferCtor, false);
42 
43     mixin(SET_CONSTRUCTOR!(Int8Array));
44     mixin(SET_CONSTRUCTOR!(Uint8Array));
45     mixin(SET_CONSTRUCTOR!(Int16Array));
46     mixin(SET_CONSTRUCTOR!(Uint16Array));
47     mixin(SET_CONSTRUCTOR!(Int32Array));
48     mixin(SET_CONSTRUCTOR!(Uint32Array));
49     mixin(SET_CONSTRUCTOR!(Float32Array));
50     mixin(SET_CONSTRUCTOR!(Float64Array));
51     mixin(SET_CONSTRUCTOR!(BigInt64Array));
52     mixin(SET_CONSTRUCTOR!(BigUint64Array));
53 }
54 
55 private string SET_CONSTRUCTOR(A)()
56 {
57     return format(q{
58         ScriptAny a%1$sCtor = new ScriptFunction("%1$s", &native_TArray_ctor!(%1$s));
59         a%1$sCtor["BYTES_PER_ELEMENT"] = ScriptAny((typeof(%1$s.data[0]).sizeof));
60         a%1$sCtor["name"] = ScriptAny("%1$s");
61         a%1$sCtor["isView"] = new ScriptFunction("%1$s.isView", &native_TArray_s_isView!%1$s);
62         a%1$sCtor["from"] = new ScriptFunction("%1$s.from", &native_TArray_s_from!%1$s);
63         a%1$sCtor["of"] = new ScriptFunction("%1$s.of", &native_TArray_s_of!%1$s);
64         a%1$sCtor["prototype"] = get%1$sPrototype();
65         a%1$sCtor["prototype"]["constructor"] = a%1$sCtor;
66         interpreter.forceSetGlobal("%1$s", a%1$sCtor, false);
67     }, A.stringof);
68 }
69 
70 /**
71  * Base class
72  */
73 abstract class AbstractArrayBuffer
74 {
75     /// Constructor
76     this(Type t)
77     {
78         _type = t;
79     }
80 
81     /// An optimized RTTI
82     enum Type 
83     {
84         ARRAY_BUFFER, // untyped view
85         INT8_ARRAY,
86         UINT8_ARRAY,
87         INT16_ARRAY,
88         UINT16_ARRAY,
89         INT32_ARRAY,
90         UINT32_ARRAY,
91         FLOAT32_ARRAY,
92         FLOAT64_ARRAY,
93         BIGINT64_ARRAY,
94         BIGUINT64_ARRAY
95     }
96 
97     abstract long getIndex(long index); // convert -1 to appropriate index
98 
99     /// read-only type property
100     auto type() const { return _type; }
101 
102     private Type _type;
103 
104     /// whether or not the instance is a view or read-only
105     bool isView() const { return _type != Type.ARRAY_BUFFER; }
106 
107     override string toString() const
108     {
109         throw new Exception("Each base class is supposed to override toString");
110     }
111 }
112 
113 /// ArrayBuffer
114 class ScriptArrayBuffer : AbstractArrayBuffer
115 {
116     /// constructor
117     this(size_t length)
118     {
119         super(Type.ARRAY_BUFFER);
120         data = new ubyte[length];
121     }
122 
123     /// construct from binary data
124     this(ubyte[] rawBytes)
125     {
126         super(Type.ARRAY_BUFFER);
127         data = rawBytes;
128     }
129 
130     override long getIndex(long index) const { return -1; }
131 
132     /// read-only data
133     ubyte[] data;
134 
135     override string toString() const
136     {
137         return format("%s", data);
138     }
139 }
140 
141 
142 private ScriptObject _arrayBufferPrototype;
143 
144 /// Gets the ArrayBuffer prototype
145 ScriptObject getArrayBufferPrototype()
146 {
147     if(_arrayBufferPrototype is null)
148     {
149         _arrayBufferPrototype = new ScriptObject("ArrayBuffer", null);
150         _arrayBufferPrototype.addGetterProperty("byteLength", new ScriptFunction(
151             "ArrayBuffer.prototype.byteLength", &native_ArrayBuffer_p_byteLength));
152         _arrayBufferPrototype["isView"] = new ScriptFunction("ArrayBuffer.protototype.isView",
153                 &native_TArray_isView!ScriptArrayBuffer);
154         _arrayBufferPrototype["slice"] = new ScriptFunction("ArrayBuffer.prototype.slice",
155                 &native_ArrayBuffer_slice);
156     }
157     return _arrayBufferPrototype;
158 }
159 
160 /// macro for defining all the subtypes
161 private string DEFINE_BUFFER_VIEW(string className, AbstractArrayBuffer.Type t, alias ElementType)()
162 {
163     import std.conv: to;
164     return format(q{
165 class %1$s : AbstractArrayBuffer
166 {
167     import std.conv: to;
168     this(size_t length)
169     {
170         super(%2$s);
171         data = new %3$s[length];
172     }
173 
174     override long getIndex(long index) const 
175     {
176         if(index < 0)
177             index = data.length + index;
178         if(index < 0 || index >= data.length)
179             return -1;
180         return index;      
181     }
182 
183     override string toString() const 
184     {
185         return to!string(data);
186     }
187 
188     size_t byteOffset;
189     %3$s[] data;
190 }
191 
192 private ScriptObject _a%1$sPrototype;
193 ScriptObject get%1$sPrototype() 
194 {
195     if(_a%1$sPrototype is null)
196     {
197         _a%1$sPrototype = new ScriptObject("%1$s", null);
198         _a%1$sPrototype["at"] = new ScriptFunction("%1$s.prototype.at",
199                 &native_TArray_at!%1$s);
200         _a%1$sPrototype.addGetterProperty("buffer", new ScriptFunction("%1$s.prototype.buffer",
201                 &native_TArray_p_buffer!%1$s));
202         _a%1$sPrototype.addGetterProperty("byteLength", new ScriptFunction("%1$s.prototype.byteLength",
203                 &native_TArray_p_byteLength!%1$s));
204         _a%1$sPrototype.addGetterProperty("byteOffset", new ScriptFunction("%1$s.prototype.byteOffset",
205                 &native_TArray_p_byteOffset!%1$s));
206         _a%1$sPrototype["copyWithin"] = new ScriptFunction("%1$s.prototype.copyWithin",
207                 &native_TArray_copyWithin!%1$s);
208         _a%1$sPrototype["entries"] = new ScriptFunction("%1$s.prototype.entries",
209                 &native_TArray_entries!%1$s);
210         _a%1$sPrototype["every"] = new ScriptFunction("%1$s.prototype.every",
211                 &native_TArray_every!%1$s);
212         _a%1$sPrototype["fill"] = new ScriptFunction("%1$s.prototype.fill",
213                 &native_TArray_fill!%1$s);
214         _a%1$sPrototype["filter"] = new ScriptFunction("%1$s.prototype.filter",
215                 &native_TArray_filter!%1$s);
216         _a%1$sPrototype["find"] = new ScriptFunction("%1$s.prototype.find",
217                 &native_TArray_find!%1$s);
218         _a%1$sPrototype["findIndex"] = new ScriptFunction("%1$s.prototype.findIndex",
219                 &native_TArray_findIndex!%1$s);
220         _a%1$sPrototype["forEach"] = new ScriptFunction("%1$s.prototype.forEach",
221                 &native_TArray_forEach!%1$s);
222         _a%1$sPrototype["includes"] = new ScriptFunction("%1$s.prototype.includes",
223                 &native_TArray_includes!%1$s);
224         _a%1$sPrototype["indexOf"] = new ScriptFunction("%1$s.prototype.indexOf",
225                 &native_TArray_indexOf!%1$s);
226         _a%1$sPrototype["isView"] = new ScriptFunction("%1$s.prototype.isView",
227                 &native_TArray_isView!%1$s);
228         _a%1$sPrototype["join"] = new ScriptFunction("%1$s.prototype.join",
229                 &native_TArray_join!%1$s);
230         _a%1$sPrototype["keys"] = new ScriptFunction("%1$s.prototype.keys",
231                 &native_TArray_keys!%1$s);
232         _a%1$sPrototype["lastIndexOf"] = new ScriptFunction("%1$s.prototype.lastIndexOf",
233                 &native_TArray_lastIndexOf!%1$s);
234         _a%1$sPrototype.addGetterProperty("length", new ScriptFunction("%1$s.prototype.length",
235                 &native_TArray_p_length!%1$s));
236         _a%1$sPrototype["map"] = new ScriptFunction("%1$s.prototype.map",
237                 &native_TArray_map!%1$s);
238         _a%1$sPrototype.addGetterProperty("name", new ScriptFunction("%1$s.prototype.name",
239                 &native_TArray_p_name!%1$s));
240         _a%1$sPrototype["reduce"] = new ScriptFunction("%1$s.prototype.reduce",
241                 &native_TArray_reduce!%1$s);
242         _a%1$sPrototype["reduceRight"] = new ScriptFunction("%1$s.prototype.reduceRight",
243                 &native_TArray_reduceRight!%1$s);
244         _a%1$sPrototype["reverse"] = new ScriptFunction("%1$s.prototype.reverse",
245                 &native_TArray_reverse!%1$s);
246         _a%1$sPrototype["set"] = new ScriptFunction("%1$s.prototype.set",
247                 &native_TArray_set!%1$s);
248         _a%1$sPrototype["slice"] = new ScriptFunction("%1$s.prototype.slice",
249                 &native_TArray_slice!%1$s);
250         _a%1$sPrototype["some"] = new ScriptFunction("%1$s.prototype.some",
251                 &native_TArray_some!%1$s);
252         _a%1$sPrototype["sort"] = new ScriptFunction("%1$s.prototype.sort",
253                 &native_TArray_sort!%1$s);
254         _a%1$sPrototype["subarray"] = new ScriptFunction("%1$s.prototype.subarray",
255                 &native_TArray_subarray!%1$s);
256         _a%1$sPrototype["values"] = new ScriptFunction("%1$s.prototype.values",
257                 &native_TArray_values!%1$s);
258     }
259     return _a%1$sPrototype;
260 }
261 
262     }, className, t.stringof, ElementType.stringof);
263 }
264 
265 mixin(DEFINE_BUFFER_VIEW!("Int8Array", AbstractArrayBuffer.Type.INT8_ARRAY, byte));
266 mixin(DEFINE_BUFFER_VIEW!("Uint8Array", AbstractArrayBuffer.Type.UINT8_ARRAY, ubyte));
267 mixin(DEFINE_BUFFER_VIEW!("Int16Array", AbstractArrayBuffer.Type.INT16_ARRAY, short));
268 mixin(DEFINE_BUFFER_VIEW!("Uint16Array", AbstractArrayBuffer.Type.UINT16_ARRAY, ushort));
269 mixin(DEFINE_BUFFER_VIEW!("Int32Array", AbstractArrayBuffer.Type.INT32_ARRAY, int));
270 mixin(DEFINE_BUFFER_VIEW!("Uint32Array", AbstractArrayBuffer.Type.UINT32_ARRAY, uint));
271 mixin(DEFINE_BUFFER_VIEW!("Float32Array", AbstractArrayBuffer.Type.FLOAT32_ARRAY, float));
272 mixin(DEFINE_BUFFER_VIEW!("Float64Array", AbstractArrayBuffer.Type.FLOAT64_ARRAY, double));
273 mixin(DEFINE_BUFFER_VIEW!("BigInt64Array", AbstractArrayBuffer.Type.BIGINT64_ARRAY, long));
274 mixin(DEFINE_BUFFER_VIEW!("BigUint64Array", AbstractArrayBuffer.Type.BIGUINT64_ARRAY, ulong));
275 
276 private ScriptAny native_ArrayBuffer_ctor(Environment env, ScriptAny* thisObj,
277                                           ScriptAny[] args, ref NativeFunctionError nfe)
278 {
279     if(!thisObj.isObject)
280         throw new ScriptRuntimeException("ArrayBuffer constructor must be called with new");
281     if(args.length == 0)
282     {
283         auto ab = new ScriptArrayBuffer(0);
284         thisObj.toValue!ScriptObject.nativeObject = ab;
285     }
286     else if(args.length > 0 && args[0].isNumber)
287     {
288         size_t size = args.length > 0 ? args[0].toValue!size_t : 0;
289         auto ab = new ScriptArrayBuffer(size);
290         thisObj.toValue!ScriptObject.nativeObject = ab;
291     }
292     else if(args.length > 0 && args[0].isNativeObjectType!AbstractArrayBuffer)
293     {
294         auto ab = new ScriptArrayBuffer(0);
295         auto aab = args[0].toNativeObject!AbstractArrayBuffer; // @suppress(dscanner.suspicious.unmodified)
296         final switch(aab.type)
297         {
298         case AbstractArrayBuffer.Type.ARRAY_BUFFER:
299             ab.data = (cast(ScriptArrayBuffer)aab).data;
300             break;
301         case AbstractArrayBuffer.Type.INT8_ARRAY: {
302             auto a = cast(Int8Array)aab;
303             ab.data = (cast(ubyte*)(a.data.ptr))[0..a.data.length];
304             break;
305         }
306         case AbstractArrayBuffer.Type.UINT8_ARRAY: {
307             auto a = cast(Uint8Array)aab;
308             ab.data = a.data;
309             break;
310         }
311         case AbstractArrayBuffer.Type.INT16_ARRAY: {
312             auto a = cast(Int16Array)aab;
313             ab.data = (cast(ubyte*)(a.data.ptr))[0..a.data.length*short.sizeof];
314             break;
315         }
316         case AbstractArrayBuffer.Type.UINT16_ARRAY: {
317             auto a = cast(Uint16Array)aab;
318             ab.data = (cast(ubyte*)(a.data.ptr))[0..a.data.length*ushort.sizeof];
319             break;
320         }
321         case AbstractArrayBuffer.Type.INT32_ARRAY: {
322             auto a = cast(Int32Array)aab;
323             ab.data = (cast(ubyte*)(a.data.ptr))[0..a.data.length*int.sizeof];
324             break;
325         }
326         case AbstractArrayBuffer.Type.UINT32_ARRAY: {
327             auto a = cast(Uint32Array)aab;
328             ab.data = (cast(ubyte*)(a.data.ptr))[0..a.data.length*uint.sizeof];
329             break;
330         }
331         case AbstractArrayBuffer.Type.FLOAT32_ARRAY: {
332             auto a = cast(Float32Array)aab;
333             ab.data = (cast(ubyte*)(a.data.ptr))[0..a.data.length*float.sizeof];
334             break;
335         }
336         case AbstractArrayBuffer.Type.FLOAT64_ARRAY: {
337             auto a = cast(Float64Array)aab;
338             ab.data = (cast(ubyte*)(a.data.ptr))[0..a.data.length*double.sizeof];
339             break;
340         }
341         case AbstractArrayBuffer.Type.BIGINT64_ARRAY: {
342             auto a = cast(BigInt64Array)aab;
343             ab.data = (cast(ubyte*)(a.data.ptr))[0..a.data.length*long.sizeof];
344             break;
345         }
346         case AbstractArrayBuffer.Type.BIGUINT64_ARRAY: {
347             auto a = cast(BigUint64Array)aab;
348             ab.data = (cast(ubyte*)(a.data.ptr))[0..a.data.length*ulong.sizeof];
349             break;
350         }
351         }
352         thisObj.toValue!ScriptObject.nativeObject = ab;
353     }
354     return ScriptAny.UNDEFINED;
355 }
356 
357 private ScriptAny native_ArrayBuffer_p_byteLength(Environment env, ScriptAny* thisObj,
358                                                   ScriptAny[] args, ref NativeFunctionError nfe)
359 {
360     auto ab = thisObj.toNativeObject!ScriptArrayBuffer;
361     if(ab is null)
362         throw new ScriptRuntimeException("This is not an ArrayBuffer");
363     return ScriptAny(ab.data.length);    
364 }
365 
366 private ScriptAny native_ArrayBuffer_s_isView(Environment env, ScriptAny* thisObj,
367                                               ScriptAny[] args, ref NativeFunctionError nfe)
368 {
369     return ScriptAny(false);
370 }
371 
372 private ScriptAny native_ArrayBuffer_slice(Environment env, ScriptAny* thisObj,
373                                            ScriptAny[] args, ref NativeFunctionError nfe)
374 {
375     auto ab = thisObj.toNativeObject!ScriptArrayBuffer;
376     if(ab is null)
377         throw new ScriptRuntimeException("This is not an ArrayBuffer");
378     long start = args.length > 0 ? args[0].toValue!long : 0;
379     long end = args.length > 1 ? args[1].toValue!long : ab.data.length;
380     if(start < 0) start += ab.data.length;
381     if(end < 0) end += ab.data.length;
382     if(start < 0 || start >= ab.data.length)
383         start = 0;
384     if(end < 0 || end > ab.data.length)
385         end = ab.data.length;
386     if(end < start)
387     {
388         immutable temp = end;
389         end = start;
390         start = temp;
391     }
392     auto sliced = new ScriptArrayBuffer(0);
393     sliced.data = ab.data[start..end];
394     return ScriptAny(new ScriptObject("ArrayBuffer", getArrayBufferPrototype, sliced));
395 }
396 
397 private ScriptAny native_TArray_ctor(A)(Environment env, ScriptAny* thisObj,
398                                         ScriptAny[] args, ref NativeFunctionError nfe)
399 {
400     import mildew.types.bindings: isIterable, native_Array_s_from;
401     alias E = typeof(A.data[0]);
402 
403     if(!thisObj.isObject)
404         throw new ScriptRuntimeException(A.stringof ~ " constructor must be called with new");
405     if(args.length < 1)
406     {
407         thisObj.toValue!ScriptObject.nativeObject = new A(0);
408     }
409     else if(args[0].isNumber)
410     {
411         size_t size = args.length > 0 ? args[0].toValue!size_t : 0;
412         auto a = new A(size);
413         thisObj.toValue!ScriptObject.nativeObject = a;
414     }
415     else if(args[0].isNativeObjectType!ScriptArrayBuffer)
416     {
417         auto arrayBuffer = args[0].toNativeObject!ScriptArrayBuffer;
418         if(arrayBuffer.data.length == 0)
419         {
420             thisObj.toValue!ScriptObject.nativeObject = new A(0);
421             return ScriptAny.UNDEFINED;
422         }
423 
424         auto offset = args.length > 1 ? args[1].toValue!size_t : 0;
425         if(offset >= arrayBuffer.data.length)
426             offset = 0;
427         auto data = arrayBuffer.data[offset..$];
428         size_t plusOne = data.length % E.sizeof == 0 ? 0 : 1;
429 
430         auto a = new A(data.length / E.sizeof + plusOne * E.sizeof);
431         a.byteOffset = offset;
432         static if(is(A==Uint8Array))
433             a.data[] = data[0..$];
434         else 
435             a.data[] = (cast(E*)data.ptr)[0..data.length/E.sizeof];
436         thisObj.toValue!ScriptObject.nativeObject = a;
437     }
438     else if(isIterable(args[0]))
439     {
440         immutable arr = native_TArray_s_from!A(env, thisObj, [args[0]], nfe);
441         auto a = arr.toNativeObject!A; // @suppress(dscanner.suspicious.unmodified)
442         thisObj.toValue!ScriptObject.nativeObject = a;
443     }
444     return ScriptAny.UNDEFINED;
445 }
446 
447 private ScriptAny native_TArray_at(A)(Environment env, ScriptAny* thisObj,
448                                       ScriptAny[] args, ref NativeFunctionError nfe)
449 {
450     auto a  = thisObj.toNativeObject!A;
451     if(a is null)
452         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
453     long index = args[0].toValue!long;
454     if(index < 0) index += a.data.length;
455     if(index < 0 || index >= a.data.length)
456         return ScriptAny.UNDEFINED;
457     return ScriptAny(a.data[index]);
458 }
459 
460 private ScriptAny native_TArray_p_buffer(A)(Environment env, ScriptAny* thisObj,
461                                             ScriptAny[] args, ref NativeFunctionError nfe)
462 {
463     auto a  = thisObj.toNativeObject!A;
464     if(a is null)
465         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
466     alias E = typeof(A.data[0]);
467     auto arrayBuffer = new ScriptArrayBuffer((cast(ubyte*)a.data.ptr)[0..E.sizeof*a.data.length]);
468     return ScriptAny(new ScriptObject("ArrayBuffer", getArrayBufferPrototype, arrayBuffer));
469 }
470 
471 private ScriptAny native_TArray_p_byteLength(A)(Environment env, ScriptAny* thisObj,
472                                                 ScriptAny[] args, ref NativeFunctionError nfe)
473 {
474     auto a = thisObj.toNativeObject!A;
475     if(a is null)
476         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
477     alias E = typeof(A.data[0]);
478     return ScriptAny(a.data.length * E.sizeof);
479 }
480 
481 private ScriptAny native_TArray_p_byteOffset(A)(Environment env, ScriptAny* thisObj,
482                                                 ScriptAny[] args, ref NativeFunctionError nfe)
483 {
484     auto a = thisObj.toNativeObject!A;
485     if(a is null)
486         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
487     return ScriptAny(a.byteOffset);
488 }
489 
490 
491 private ScriptAny native_TArray_copyWithin(A)(Environment env, ScriptAny* thisObj,
492                                               ScriptAny[] args, ref NativeFunctionError nfe)
493 {
494     auto a = thisObj.toNativeObject!A;
495     if(a is null)
496         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
497     long target = args.length > 0 ? args[0].toValue!long : a.data.length;
498     long start = args.length > 1 ? args[1].toValue!long : 0;
499     long end = args.length > 2 ? args[2].toValue!long : a.data.length;
500 
501     if(target < 0) target += a.data.length;
502     if(start < 0) start += a.data.length;
503     if(end < 0) end += a.data.length;
504 
505     if(target < 0 || target >= a.data.length)
506         target = a.data.length;
507     if(start < 0 || start >= a.data.length)
508         start  = 0;
509     if(end < 0 || end >= a.data.length)
510         end = a.data.length;
511     if(end <= start)
512         return *thisObj;
513     for(long i = 0; i < (end - start); ++i)
514     {
515         if(i + target >= a.data.length || i + start >= a.data.length)
516             break;
517         a.data[i+target] = a.data[i+start];
518     }
519     return *thisObj;
520 }
521 
522 private ScriptAny native_TArray_entries(A)(Environment env, ScriptAny* thisObj,
523                                            ScriptAny[] args, ref NativeFunctionError nfe)
524 {
525     import std.concurrency: yield;
526 
527     auto a = thisObj.toNativeObject!A; // @suppress(dscanner.suspicious.unmodified)
528     if(a is null)
529         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
530     auto genFunc = new ScriptFunction("Iterator", 
531         delegate ScriptAny(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe)
532         {
533             auto arr = args[0].toNativeObject!A;
534             foreach(index, value ; arr.data)
535             {
536                 auto entry = new ScriptAny[2];
537                 entry[0] = ScriptAny(index);
538                 entry[1] = ScriptAny(value);
539                 yield!ScriptAny(ScriptAny(entry));
540             }
541             return ScriptAny.UNDEFINED;
542         }
543     );
544     auto generator = new ScriptGenerator(env, genFunc, [*thisObj]);
545     auto iterator = new ScriptObject("Iterator", getGeneratorPrototype, generator);
546     return ScriptAny(iterator);
547 }
548 
549 private ScriptAny native_TArray_every(A)(Environment env, ScriptAny* thisObj,
550                                          ScriptAny[] args, ref NativeFunctionError nfe)
551 {
552     auto a = thisObj.toNativeObject!A; // @suppress(dscanner.suspicious.unmodified)
553     if(a is null)
554         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
555     if(args.length < 1)
556         return ScriptAny(false);
557     if(args[0].type != ScriptAny.Type.FUNCTION)
558         return ScriptAny(false);
559     auto theThisArg = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
560     bool result = true;
561     size_t counter = 0;
562     foreach(element ; a.data)
563     {
564         auto temp = native_Function_call(env, &args[0], 
565             [ theThisArg, ScriptAny(element), ScriptAny(counter), *thisObj ], nfe);
566         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
567             return temp;
568         result = result && temp;
569         if(!result)
570             return ScriptAny(result);
571         ++counter;
572     }
573     return ScriptAny(result);
574 }
575 
576 private ScriptAny native_TArray_fill(A)(Environment env, ScriptAny* thisObj,
577                                         ScriptAny[] args, ref NativeFunctionError nfe)
578 {
579     alias E = typeof(A.data[0]);
580     auto a = thisObj.toNativeObject!A;
581     if(a is null)
582         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
583     if(args.length < 1)
584         return *thisObj;
585 
586     long start = args.length > 1 ? args[1].toValue!long : 0;
587     long end = args.length > 2 ? args[2].toValue!long : a.data.length;
588 
589     if(start < 0) start += a.data.length;
590     if(end < 0) end += a.data.length;
591 
592     if(start < 0 || start >= a.data.length)
593         start = 0;
594     if(end < 0 || end >= a.data.length)
595         end = a.data.length;
596     for(size_t i = start; i < end; ++i)
597         a.data[i] = args[0].toValue!E;
598     return *thisObj;
599 }
600 
601 private ScriptAny native_TArray_filter(A)(Environment env, ScriptAny* thisObj,
602                                           ScriptAny[] args, ref NativeFunctionError nfe)
603 {
604     auto a = thisObj.toNativeObject!A;
605     if(a is null)
606         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
607     if(args.length < 1)
608     {
609         nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS;
610         return ScriptAny.UNDEFINED;
611     }
612     if(args[0].type != ScriptAny.Type.FUNCTION)
613     {
614         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
615         return ScriptAny.UNDEFINED;
616     }
617     ScriptAny thisToUse = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
618     auto result = new A(0);
619     size_t counter = 0;
620     foreach(element ; a.data)
621     {
622         auto temp = native_Function_call(env, &args[0], 
623             [thisToUse, ScriptAny(element), ScriptAny(counter), *thisObj], nfe);
624         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
625             return temp;
626         if(temp)
627             result.data ~= element;
628         ++counter;
629     }
630     return ScriptAny(new ScriptObject(A.stringof, thisObj.toValue!ScriptObject.prototype, result));
631 }
632 
633 private ScriptAny native_TArray_find(A)(Environment env, ScriptAny* thisObj, 
634                                         ScriptAny[] args, ref NativeFunctionError nfe)
635 {
636     auto a = thisObj.toNativeObject!A;
637     if(a is null)
638         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
639     if(args.length < 1)
640         return ScriptAny.UNDEFINED;
641     if(args[0].type != ScriptAny.Type.FUNCTION)
642         return ScriptAny.UNDEFINED;
643     auto thisToUse = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
644     for(size_t i = 0; i < a.data.length; ++i)
645     {
646         auto temp = native_Function_call(env, &args[0], 
647             [thisToUse, ScriptAny(a.data[i]), ScriptAny(i), *thisObj], nfe);
648         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
649             return temp;
650         if(temp)
651             return ScriptAny(a.data[i]);
652     }
653 
654     return ScriptAny.UNDEFINED;
655 }
656 
657 private ScriptAny native_TArray_findIndex(A)(Environment env, ScriptAny* thisObj, 
658                                              ScriptAny[] args, ref NativeFunctionError nfe)
659 {
660     auto a = thisObj.toNativeObject!A;
661     if(a is null)
662         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
663     if(args.length < 1)
664         return ScriptAny.UNDEFINED;
665     if(args[0].type != ScriptAny.Type.FUNCTION)
666         return ScriptAny.UNDEFINED;
667     auto thisToUse = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
668     for(size_t i = 0; i < a.data.length; ++i)
669     {
670         auto temp = native_Function_call(env, &args[0], 
671             [thisToUse, ScriptAny(a.data[i]), ScriptAny(i), *thisObj], nfe);
672         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
673             return temp;
674         if(temp)
675             return ScriptAny(i);
676     }
677 
678     return ScriptAny(-1);
679 }
680 
681 private ScriptAny native_TArray_forEach(A)(Environment env, ScriptAny* thisObj,
682                                            ScriptAny[] args, ref NativeFunctionError nfe)
683 {
684     auto a = thisObj.toNativeObject!A;
685     if(a is null)
686         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
687     if(args.length < 1)
688     {
689         nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS;
690         return ScriptAny.UNDEFINED;
691     }
692     if(args[0].type != ScriptAny.Type.FUNCTION)
693     {
694         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
695         return ScriptAny.UNDEFINED;
696     }
697     auto thisToUse = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
698     for(size_t i = 0; i < a.data.length; ++i)
699     {
700         auto temp = native_Function_call(env, &args[0],
701             [thisToUse, ScriptAny(a.data[i]), ScriptAny(i), *thisObj], nfe);
702         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
703             return temp;
704     }
705     return ScriptAny.UNDEFINED;
706 }
707 
708 /**
709  * Creates an Array from any iterable
710  */
711 ScriptAny native_TArray_s_from(A)(Environment env, ScriptAny* thisObj,
712                                   ScriptAny[] args, ref NativeFunctionError nfe)
713 {
714     import std.format: format;
715     alias E = typeof(A.data[0]);
716     if(args.length < 1)
717         mixin(format("return ScriptAny(new ScriptObject(\"%1$s\", get%1$sPrototype, new A(0)));", A.stringof));
718     ScriptAny func = args.length > 1 ? args[1] : ScriptAny.UNDEFINED;
719     auto thisToUse = args.length > 2 ? args[2] : getLocalThis(env, func);
720     
721     auto result = new A(0);
722     if(args[0].type == ScriptAny.Type.ARRAY)
723     {
724         auto arr = args[0].toValue!ScriptArray.array;
725         for(size_t i = 0; i < arr.length; ++i)
726         {
727             if(func.type == ScriptAny.Type.FUNCTION)
728             {
729                 auto temp = native_Function_call(env, &func, [thisToUse, arr[i], ScriptAny(i), args[0]], nfe);
730                 if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
731                     return temp;
732                 result.data ~= temp.toValue!E;
733             }
734             else
735             {
736                 result.data ~= arr[i].toValue!E;
737             }
738         }
739     }
740     else if(args[0].type == ScriptAny.Type.STRING)
741     {
742         size_t index = 0;
743         foreach(dchar ch ; args[0].toString())
744         {
745             if(func.type == ScriptAny.Type.FUNCTION)
746             {
747                 auto temp = native_Function_call(env, &func, 
748                     [thisToUse, ScriptAny([ch]), ScriptAny(index), args[0]], nfe);
749                 if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
750                     return temp;
751                 result.data ~= temp.toValue!E;
752             }
753             else
754             {
755                 result.data ~= cast(E)ch;
756             }
757             ++index;
758         }       
759     }
760     else if(args[0].isNativeObjectType!AbstractArrayBuffer)
761     {
762         auto aab = args[0].toNativeObject!AbstractArrayBuffer; // @suppress(dscanner.suspicious.unmodified)
763         if(!aab.isView)
764             throw new ScriptRuntimeException("ArrayBuffer must be cast to view");
765         string HANDLE_TYPED_ARRAY(A)()
766         {
767             import std.format: format;
768             return format(q{
769             {
770                 auto a = cast(%1$s)aab;
771                 for(size_t i = 0; i < a.data.length; ++i)
772                 {
773                     if(func.type == ScriptAny.Type.FUNCTION)
774                     {
775                         auto temp = native_Function_call(env, &func,
776                             [thisToUse, ScriptAny(a.data[i]), ScriptAny(i), args[0]], nfe);
777                         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
778                             return temp;
779                         result.data ~= temp.toValue!E;
780                     }
781                     else 
782                     {
783                         result.data ~= cast(E)a.data[i];
784                     }
785                 }
786             }
787             }, A.stringof);
788         }
789         final switch(aab.type)
790         {
791         case AbstractArrayBuffer.Type.ARRAY_BUFFER:
792             break; // already handled
793         case AbstractArrayBuffer.Type.INT8_ARRAY:
794             mixin(HANDLE_TYPED_ARRAY!Int8Array);
795             break;
796         case AbstractArrayBuffer.Type.UINT8_ARRAY:
797             mixin(HANDLE_TYPED_ARRAY!Uint8Array);
798             break;
799         case AbstractArrayBuffer.Type.INT16_ARRAY:
800             mixin(HANDLE_TYPED_ARRAY!Int16Array);
801             break;
802         case AbstractArrayBuffer.Type.UINT16_ARRAY:
803             mixin(HANDLE_TYPED_ARRAY!Uint16Array);
804             break;
805         case AbstractArrayBuffer.Type.INT32_ARRAY:
806             mixin(HANDLE_TYPED_ARRAY!Int32Array);
807             break;
808         case AbstractArrayBuffer.Type.UINT32_ARRAY:
809             mixin(HANDLE_TYPED_ARRAY!Uint32Array);
810             break;
811         case AbstractArrayBuffer.Type.FLOAT32_ARRAY:
812             mixin(HANDLE_TYPED_ARRAY!Float32Array);
813             break;
814         case AbstractArrayBuffer.Type.FLOAT64_ARRAY:
815             mixin(HANDLE_TYPED_ARRAY!Float64Array);
816             break;
817         case AbstractArrayBuffer.Type.BIGINT64_ARRAY:
818             mixin(HANDLE_TYPED_ARRAY!BigInt64Array);
819             break;
820         case AbstractArrayBuffer.Type.BIGUINT64_ARRAY:
821             mixin(HANDLE_TYPED_ARRAY!BigUint64Array);
822             break;
823         }
824     }
825     else if(args[0].isNativeObjectType!ScriptGenerator)
826     {
827         auto nextIteration = native_Generator_next(env, &args[0], [], nfe).toValue!ScriptObject;
828         size_t counter = 0;
829         while(!nextIteration["done"])
830         {
831             auto value = nextIteration["value"];
832             if(func.type == ScriptAny.Type.FUNCTION)
833             {
834                 auto temp = native_Function_call(env, &func, [thisToUse, value, ScriptAny(counter), args[0]], nfe);
835                 if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
836                     return temp;
837                 result.data ~= temp.toValue!E;
838             }
839             else
840             {
841                 result.data ~= value.toValue!E;
842             }
843             ++counter;
844             nextIteration = native_Generator_next(env, &args[0], [], nfe).toValue!ScriptObject;
845         }
846     }
847 
848     mixin(format("return ScriptAny(new ScriptObject(\"%1$s\", get%1$sPrototype, result));", A.stringof));
849 }
850 
851 private ScriptAny native_TArray_includes(A)(Environment env, ScriptAny* thisObj,
852                                             ScriptAny[] args, ref NativeFunctionError nfe)
853 {
854     alias E = typeof(A.data[0]);
855     auto a = thisObj.toNativeObject!A;
856     if(a is null)
857         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
858     if(a.data.length < 1)
859         return ScriptAny(false);
860     long indexToStart = args.length > 1 ? args[1].toValue!long : 0;
861     if(indexToStart < 0) indexToStart += a.data.length;
862 
863     if(indexToStart < 0 || indexToStart >= a.data.length)
864         indexToStart = a.data.length;
865     for(size_t i = indexToStart; i < a.data.length; ++i)
866         if(args[0].toValue!E == a.data[i])
867             return ScriptAny(true);
868     return ScriptAny(false);
869 }
870 
871 private ScriptAny native_TArray_indexOf(A)(Environment env, ScriptAny* thisObj,
872                                             ScriptAny[] args, ref NativeFunctionError nfe)
873 {
874     alias E = typeof(A.data[0]);
875     auto a = thisObj.toNativeObject!A;
876     if(a is null)
877         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
878     if(a.data.length < 1)
879         return ScriptAny(-1);
880     long indexToStart = args.length > 1 ? args[1].toValue!long : 0;
881     if(indexToStart < 0) indexToStart += a.data.length;
882 
883     if(indexToStart < 0 || indexToStart >= a.data.length)
884         indexToStart = a.data.length;
885     immutable value = args[0].toValue!E;
886     for(size_t i = indexToStart; i < a.data.length; ++i)
887         if(value == a.data[i])
888             return ScriptAny(i);
889     return ScriptAny(-1);
890 }
891 
892 // only use this for typed arrays and not buffer
893 private ScriptAny native_TArray_s_isView(A)(Environment env, ScriptAny* thisObj,
894                                             ScriptAny[] args, ref NativeFunctionError nfe)
895 {
896     return ScriptAny(true);
897 }
898 
899 private ScriptAny native_TArray_isView(A)(Environment env, ScriptAny* thisObj,
900                                           ScriptAny[] args, ref NativeFunctionError nfe)
901 {
902     auto a = thisObj.toNativeObject!A;
903     if(a is null)
904         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
905     return ScriptAny(a.isView());
906 }
907 
908 private ScriptAny native_TArray_join(A)(Environment env, ScriptAny* thisObj, 
909                                         ScriptAny[] args, ref NativeFunctionError nfe)
910 {
911     import std.conv: to;
912     auto a = thisObj.toNativeObject!A;
913     if(a is null)
914         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
915     auto join = ",";
916     if(args.length > 0)
917         join = args[0].toString();
918     string result = "";
919     for(size_t i = 0; i < a.data.length; ++i)
920     {
921         result ~= to!string(a.data[i]);
922         if(i < a.data.length - 1)
923             result ~= join;
924     }
925     return ScriptAny(result);
926 }
927 
928 private ScriptAny native_TArray_keys(A)(Environment env, ScriptAny* thisObj,
929                                         ScriptAny[] args, ref NativeFunctionError nfe)
930 {
931     import std.concurrency: yield;
932     auto a = thisObj.toNativeObject!A; // @suppress(dscanner.suspicious.unmodified)
933     if(a is null)
934         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
935     auto genFunc = new ScriptFunction("Iterator", 
936         delegate ScriptAny(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe)
937         {
938             auto a = args[0].toNativeObject!A;
939             foreach(key, value ; a.data)
940                 yield!ScriptAny(ScriptAny(key));
941             return ScriptAny.UNDEFINED;
942         }
943     );
944     auto generator = new ScriptGenerator(env, genFunc, [ *thisObj ]);
945     auto iterator = new ScriptObject("Iterator", getGeneratorPrototype, generator);
946     return ScriptAny(iterator);
947 }
948 
949 private ScriptAny native_TArray_lastIndexOf(A)(Environment env, ScriptAny* thisObj,
950                                                ScriptAny[] args, ref NativeFunctionError nfe)
951 {
952     alias E = typeof(A.data[0]);
953     auto a = thisObj.toNativeObject!A;
954     if(a is null)
955         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
956     if(args.length < 1)
957         return ScriptAny(-1);
958     long indexToStart = args.length > 1 ? args[1].toValue!long : a.data.length - 1;
959     if(indexToStart < 0) indexToStart += a.data.length;
960     if(indexToStart < 0 || indexToStart >= a.data.length)
961         indexToStart = a.data.length - 1;
962     immutable value = args[0].toValue!E;
963     if(a.data.length == 0)
964         return ScriptAny(-1);
965     for(long i = indexToStart; i >= 0; --i)
966     {
967         if(value == a.data[i])
968             return ScriptAny(i);
969     }
970     return ScriptAny(-1);    
971 }
972 
973 private ScriptAny native_TArray_p_length(A)(Environment env, ScriptAny* thisObj,
974                                             ScriptAny[] args, ref NativeFunctionError nfe)
975 {
976     auto a = thisObj.toNativeObject!A;
977     if(a is null)
978         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
979     return ScriptAny(a.data.length);
980 }
981 
982 private ScriptAny native_TArray_map(A)(Environment env, ScriptAny* thisObj,
983                                       ScriptAny[] args, ref NativeFunctionError nfe)
984 {
985     alias E = typeof(A.data[0]);
986     auto a = thisObj.toNativeObject!A;
987     if(a is null)
988         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
989     if(args.length < 1)
990     {
991         nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS;
992         return ScriptAny.UNDEFINED;
993     }
994     if(args[0].type != ScriptAny.Type.FUNCTION)
995     {
996         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
997         return ScriptAny.UNDEFINED;
998     }
999     ScriptAny thisToUse = args.length > 1 ? args[1] : ScriptAny.UNDEFINED;
1000     auto result = new A(0);
1001     for(size_t i = 0; i < a.data.length; ++i)
1002     {
1003         auto temp = native_Function_call(env, &args[0], 
1004             [thisToUse, ScriptAny(a.data[i]), ScriptAny(i), *thisObj], nfe);
1005         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
1006             return temp;
1007         result.data ~= temp.toValue!E;
1008     }
1009     return ScriptAny(new ScriptObject(A.stringof, thisObj.toValue!ScriptObject.prototype, result));
1010 }
1011 
1012 private ScriptAny native_TArray_p_name(A)(Environment env, ScriptAny* thisObj,
1013                                           ScriptAny[] args, ref NativeFunctionError nfe)
1014 {
1015     return ScriptAny(A.stringof);
1016 }
1017 
1018 private ScriptAny native_TArray_s_of(A)(Environment env, ScriptAny* thisObj,
1019                                         ScriptAny[] args, ref NativeFunctionError nfe)
1020 {
1021     alias E = typeof(A.data[0]);
1022     auto results = new A(0);
1023     foreach(arg ; args)
1024         results.data ~= arg.toValue!E;
1025     mixin(format("return ScriptAny(new ScriptObject(\"%1$s\", get%1$sPrototype, results));", A.stringof));
1026 }
1027 
1028 private ScriptAny native_TArray_reduce(A)(Environment env, ScriptAny* thisObj,
1029                                           ScriptAny[] args, ref NativeFunctionError nfe)
1030 {
1031     alias E = typeof(A.data[0]);
1032     auto a = thisObj.toNativeObject!A;
1033     if(a is null)
1034         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
1035     if(args.length < 0)
1036     { 
1037         nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS;
1038         return ScriptAny.UNDEFINED;
1039     }
1040     if(args[0].type != ScriptAny.Type.FUNCTION)
1041     {
1042         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
1043         return ScriptAny.UNDEFINED;
1044     }
1045     auto accumulator = args.length > 1 ? args[1].toValue!E : (a.data.length > 0? a.data[0] : cast(E)0);
1046     immutable start = args.length < 2 ? 0 : 1;
1047     if(a.data.length == 0 && args.length < 2)
1048         throw new ScriptRuntimeException("Reduce with no accumulator may not be called on empty array");
1049     for(size_t i = start; i < a.data.length; ++i)
1050     {
1051         accumulator = native_Function_call(env, &args[0], 
1052             [getLocalThis(env, args[0]), ScriptAny(accumulator), ScriptAny(a.data[i]), 
1053             ScriptAny(i), *thisObj], nfe).toValue!E;
1054         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
1055             return ScriptAny(accumulator);
1056     }
1057     return ScriptAny(accumulator);
1058 }
1059 
1060 private ScriptAny native_TArray_reduceRight(A)(Environment env, ScriptAny* thisObj,
1061                                                ScriptAny[] args, ref NativeFunctionError nfe)
1062 {
1063     alias E = typeof(A.data[0]);
1064     auto a = thisObj.toNativeObject!A;
1065     if(a is null)
1066         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
1067     if(args.length < 0)
1068     { 
1069         nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS;
1070         return ScriptAny.UNDEFINED;
1071     }
1072     if(args[0].type != ScriptAny.Type.FUNCTION)
1073     {
1074         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
1075         return ScriptAny.UNDEFINED;
1076     }
1077     auto accumulator = args.length > 1 ? 
1078         args[1].toValue!E : 
1079         (a.data.length > 0? a.data[a.data.length-1] : cast(E)0);
1080     immutable long start = cast(long)a.data.length - 1;
1081     if(a.data.length == 0 && args.length < 2)
1082         throw new ScriptRuntimeException("Reduce right with no accumulator may not be called on empty array");
1083     for(long i = start; i > 0; --i)
1084     {
1085         accumulator = native_Function_call(env, &args[0], 
1086             [getLocalThis(env, args[0]), ScriptAny(accumulator), ScriptAny(a.data[i-1]), 
1087             ScriptAny(i-1), *thisObj], nfe).toValue!E;
1088         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
1089             return ScriptAny(accumulator);
1090     }
1091     return ScriptAny(accumulator);
1092 }
1093 
1094 private ScriptAny native_TArray_reverse(A)(Environment env, ScriptAny* thisObj,
1095                                            ScriptAny[] args, ref NativeFunctionError nfe)
1096 {
1097     import std.algorithm.mutation: reverse;
1098     auto a = thisObj.toNativeObject!A;
1099     if(a is null)
1100         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
1101     reverse(a.data);
1102     return *thisObj;
1103 }
1104 
1105 private ScriptAny native_TArray_set(A)(Environment env, ScriptAny* thisObj,
1106                                        ScriptAny[] args, ref NativeFunctionError nfe)
1107 {
1108     alias E = typeof(A.data[0]);
1109     auto a = thisObj.toNativeObject!A;
1110     if(a is null)
1111         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
1112     if(args.length < 1)
1113     {
1114         nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS;
1115         return ScriptAny.UNDEFINED;
1116     }
1117     if(!isIterable(args[0]))
1118     {
1119         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
1120         return ScriptAny.UNDEFINED;
1121     }
1122     // do this the easy way and convert all arguments to a ScriptAny[]
1123     auto arr = native_Array_s_from(env, thisObj, [args[0]], nfe).toValue!(ScriptAny[]);
1124     immutable offset = args.length > 1 ? args[1].toValue!size_t : 0;
1125     if(offset + arr.length > a.data.length)
1126         throw new ScriptRuntimeException("Set parameter exceeds array size");
1127     for(size_t i = offset; i < offset + arr.length; ++i)
1128     {
1129         a.data[i] = arr[i-offset].toValue!E;
1130     }
1131     return ScriptAny.UNDEFINED;
1132 }
1133 
1134 private ScriptAny native_TArray_slice(A)(Environment env, ScriptAny* thisObj, 
1135                                          ScriptAny[] args, ref NativeFunctionError nfe)
1136 {
1137     auto a = thisObj.toNativeObject!A;
1138     if(a is null)
1139         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
1140     long start = args.length > 0 ? args[0].toValue!long : 0;
1141     long end = args.length > 1 ? args[1].toValue!long : a.data.length;
1142     if(start < 0) start += a.data.length;
1143     if(end < 0) end += a.data.length;
1144     if(start < 0 || start >= a.data.length)
1145         start = 0;
1146     if(end < 0 || end > a.data.length)
1147         end = a.data.length;
1148     if(end < start)
1149     {
1150         immutable temp = end;
1151         end = start;
1152         start = temp;
1153     }
1154     auto sliced = new A(end-start);
1155     sliced.data[] = a.data[start..end];
1156     return ScriptAny(new ScriptObject(A.stringof, thisObj.toValue!ScriptObject.prototype, sliced));
1157 }
1158 
1159 private ScriptAny native_TArray_some(A)(Environment env, ScriptAny* thisObj, 
1160                                         ScriptAny[] args, ref NativeFunctionError nfe)
1161 {
1162     auto a = thisObj.toNativeObject!A;
1163     if(a is null)
1164         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
1165     if(args.length < 1)
1166     {
1167         nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS;
1168         return ScriptAny.UNDEFINED;
1169     }
1170     if(args[0].type != ScriptAny.Type.FUNCTION)
1171     {
1172         nfe = NativeFunctionError.WRONG_TYPE_OF_ARG;
1173         return ScriptAny.UNDEFINED;
1174     }
1175     ScriptAny thisToUse = args.length > 1 ? args[1] : getLocalThis(env, args[0]);
1176     for(size_t i = 0; i < a.data.length; ++i)
1177     {
1178         auto temp = native_Function_call(env, &args[0], 
1179             [thisToUse, ScriptAny(a.data[i]), ScriptAny(i), *thisObj], nfe);
1180         if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR || temp)
1181             return temp;
1182     }
1183     return ScriptAny(false);
1184 }
1185 
1186 private ScriptAny native_TArray_sort(A)(Environment env, ScriptAny* thisObj,
1187                                         ScriptAny[] args, ref NativeFunctionError nfe)
1188 {
1189     import std.algorithm: sort;
1190     auto a = thisObj.toNativeObject!A;
1191     if(a is null)
1192         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
1193     if(a.data.length <= 1)
1194         return *thisObj; // already sorted if empty or one element
1195     if(args.length < 1 || args[0].type != ScriptAny.Type.FUNCTION)
1196     {
1197         sort(a.data);
1198     }
1199     else
1200     {
1201         // use bubble sort
1202         for(size_t i = 0; i < a.data.length-1; ++i)
1203         {
1204             for(size_t j = 0; j < a.data.length - i - 1; ++j)
1205             {
1206                 auto temp = native_Function_call(env, &args[0], 
1207                     [getLocalThis(env, args[0]), ScriptAny(a.data[j]), 
1208                     ScriptAny(a.data[j+1])], nfe);
1209                 if(env.g.interpreter.vm.hasException || nfe != NativeFunctionError.NO_ERROR)
1210                     return temp;
1211                 if(temp.toValue!int > 0)
1212                 {
1213                     immutable swap = a.data[j+1]; // @suppress(dscanner.suspicious.unmodified)
1214                     a.data[j+1] = a.data[j];
1215                     a.data[j] = swap;
1216                 }
1217             }
1218         }
1219     }
1220     return *thisObj;
1221 }
1222 
1223 private ScriptAny native_TArray_subarray(A)(Environment env, ScriptAny* thisObj, 
1224                                          ScriptAny[] args, ref NativeFunctionError nfe)
1225 {
1226     auto a = thisObj.toNativeObject!A;
1227     if(a is null)
1228         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
1229     long start = args.length > 0 ? args[0].toValue!long : 0;
1230     long end = args.length > 1 ? args[1].toValue!long : a.data.length;
1231     if(start < 0) start += a.data.length;
1232     if(end < 0) end += a.data.length;
1233     if(start < 0 || start >= a.data.length)
1234         start = 0;
1235     if(end < 0 || end > a.data.length)
1236         end = a.data.length;
1237     if(end < start)
1238     {
1239         immutable temp = end;
1240         end = start;
1241         start = temp;
1242     }
1243     auto sliced = new A(0);
1244     sliced.data = a.data[start..end];
1245     return ScriptAny(new ScriptObject(A.stringof, thisObj.toValue!ScriptObject.prototype, sliced));
1246 }
1247 
1248 private ScriptAny native_TArray_values(A)(Environment env, ScriptAny* thisObj,
1249                                           ScriptAny[] args, ref NativeFunctionError nfe)
1250 {
1251     import std.concurrency: yield;
1252     auto a = thisObj.toNativeObject!A; // @suppress(dscanner.suspicious.unmodified)
1253     if(a is null)
1254         throw new ScriptRuntimeException("This is not a " ~ A.stringof);
1255     auto genFunc = new ScriptFunction("Iterator",
1256         delegate ScriptAny(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe)
1257         {
1258             auto a = args[0].toNativeObject!A;
1259             foreach(value ; a.data)
1260                 yield!ScriptAny( ScriptAny(value) );
1261             return ScriptAny.UNDEFINED;
1262         }
1263     );
1264     auto generator = new ScriptGenerator(env, genFunc, [*thisObj]);
1265     auto iterator = new ScriptObject("Iterator", getGeneratorPrototype, generator);
1266     return ScriptAny(iterator);
1267 }