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