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