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 }