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