1 /* Digital Mars DMDScript source code. 2 * Copyright (c) 2000-2002 by Chromium Communications 3 * D version Copyright (c) 2004-2010 by Digital Mars 4 * Distributed under the Boost Software License, Version 1.0. 5 * (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 * written by Walter Bright 7 * http://www.digitalmars.com 8 * 9 * D2 port by Dmitry Olshansky 10 * 11 * DMDScript is implemented in the D Programming Language, 12 * http://www.digitalmars.com/d/ 13 * 14 * For a C++ implementation of DMDScript, including COM support, see 15 * http://www.digitalmars.com/dscript/cppscript.html 16 */ 17 18 module dmdscript.dglobal; 19 20 import std.uri; 21 import std.c.stdlib; 22 import std.c..string; 23 import std.stdio; 24 import std.algorithm; 25 import std.math; 26 import std.exception; 27 28 import dmdscript.script; 29 import dmdscript.protoerror; 30 import dmdscript.parse; 31 import dmdscript.text; 32 import dmdscript.dobject; 33 import dmdscript.value; 34 import dmdscript.statement; 35 import dmdscript.threadcontext; 36 import dmdscript.functiondefinition; 37 import dmdscript.scopex; 38 import dmdscript.opcodes; 39 import dmdscript.property; 40 41 import dmdscript.dstring; 42 import dmdscript.darray; 43 import dmdscript.dregexp; 44 import dmdscript.dnumber; 45 import dmdscript.dboolean; 46 import dmdscript.dfunction; 47 import dmdscript.dnative; 48 49 immutable(char)[] arg0string(Value[] arglist) 50 { 51 Value* v = arglist.length ? &arglist[0] : &vundefined; 52 return v.toString(); 53 } 54 55 /* ====================== Dglobal_eval ================ */ 56 57 void* Dglobal_eval(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 58 { 59 // ECMA 15.1.2.1 60 Value* v; 61 immutable(char)[] s; 62 FunctionDefinition fd; 63 ErrInfo errinfo; 64 void *result; 65 66 //FuncLog funclog(L"Global.eval()"); 67 68 v = arglist.length ? &arglist[0] : &vundefined; 69 if(v.getType() != TypeString) 70 { 71 Value.copy(ret, v); 72 return null; 73 } 74 s = v.toString(); 75 //writef("eval('%ls')\n", s); 76 77 // Parse program 78 TopStatement[] topstatements; 79 Parser p = new Parser("eval", s, 0); 80 if(p.parseProgram(topstatements, &errinfo)) 81 goto Lsyntaxerror; 82 83 // Analyze, generate code 84 fd = new FunctionDefinition(topstatements); 85 fd.iseval = 1; 86 { 87 Scope sc; 88 sc.ctor(fd); 89 sc.src = s; 90 fd.semantic(&sc); 91 errinfo = sc.errinfo; 92 sc.dtor(); 93 } 94 if(errinfo.message) 95 goto Lsyntaxerror; 96 fd.toIR(null); 97 98 // Execute code 99 Value[] locals; 100 Value[] p1 = null; 101 102 Value* v1 = null; 103 static ntry = 0; 104 105 if(fd.nlocals < 128) 106 v1 = cast(Value*)alloca(fd.nlocals * Value.sizeof); 107 if(v1) 108 locals = v1[0 .. fd.nlocals]; 109 else 110 { 111 p1 = new Value[fd.nlocals]; 112 locals = p1; 113 } 114 115 116 version(none) 117 { 118 Array scopex; 119 scopex.reserve(cc.scoperoot + fd.withdepth + 2); 120 for(uint u = 0; u < cc.scoperoot; u++) 121 scopex.push(cc.scopex.data[u]); 122 123 Array *scopesave = cc.scopex; 124 cc.scopex = &scopex; 125 Dobject variablesave = cc.variable; 126 cc.variable = cc.global; 127 128 fd.instantiate(cc.variable, 0); 129 130 // The this value is the same as the this value of the 131 // calling context. 132 result = IR.call(cc, othis, fd.code, ret, locals); 133 134 delete p1; 135 cc.variable = variablesave; 136 cc.scopex = scopesave; 137 return result; 138 } 139 else 140 { 141 142 // The scope chain is initialized to contain the same objects, 143 // in the same order, as the calling context's scope chain. 144 // This includes objects added to the calling context's 145 // scope chain by WithStatement. 146 // cc.scopex.reserve(fd.withdepth); 147 148 // Variable instantiation is performed using the calling 149 // context's variable object and using empty 150 // property attributes 151 fd.instantiate(cc.scopex, cc.variable, 0); 152 153 // The this value is the same as the this value of the 154 // calling context. 155 assert(cc.callerothis); 156 result = IR.call(cc, cc.callerothis, fd.code, ret, locals.ptr); 157 if(p1) 158 delete p1; 159 fd = null; 160 // if (result) writef("result = '%s'\n", (cast(Value* )result).toString()); 161 return result; 162 } 163 164 Lsyntaxerror: 165 Dobject o; 166 167 // For eval()'s, use location of caller, not the string 168 errinfo.linnum = 0; 169 170 ret.putVundefined(); 171 o = new syntaxerror.D0(&errinfo); 172 Value* v2 = new Value; 173 v2.putVobject(o); 174 return v2; 175 } 176 177 /* ====================== Dglobal_parseInt ================ */ 178 179 void* Dglobal_parseInt(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 180 { 181 // ECMA 15.1.2.2 182 Value* v2; 183 immutable(char) * s; 184 immutable(char) * z; 185 d_int32 radix; 186 int sign = 1; 187 d_number number; 188 size_t i; 189 d_string string; 190 191 string = arg0string(arglist); 192 193 //writefln("Dglobal_parseInt('%s')", string); 194 195 while(i < string.length) 196 { 197 size_t idx = i; 198 dchar c = std.utf.decode(string, idx); 199 if(!isStrWhiteSpaceChar(c)) 200 break; 201 i = idx; 202 } 203 s = string.ptr + i; 204 i = string.length - i; 205 206 if(i) 207 { 208 if(*s == '-') 209 { 210 sign = -1; 211 s++; 212 i--; 213 } 214 else if(*s == '+') 215 { 216 s++; 217 i--; 218 } 219 } 220 221 radix = 0; 222 if(arglist.length >= 2) 223 { 224 v2 = &arglist[1]; 225 radix = v2.toInt32(); 226 } 227 228 if(radix) 229 { 230 if(radix < 2 || radix > 36) 231 { 232 number = d_number.nan; 233 goto Lret; 234 } 235 if(radix == 16 && i >= 2 && *s == '0' && 236 (s[1] == 'x' || s[1] == 'X')) 237 { 238 s += 2; 239 i -= 2; 240 } 241 } 242 else if(i >= 1 && *s != '0') 243 { 244 radix = 10; 245 } 246 else if(i >= 2 && (s[1] == 'x' || s[1] == 'X')) 247 { 248 radix = 16; 249 s += 2; 250 i -= 2; 251 } 252 else 253 radix = 8; 254 255 number = 0; 256 for(z = s; i; z++, i--) 257 { 258 d_int32 n; 259 tchar c; 260 261 c = *z; 262 if('0' <= c && c <= '9') 263 n = c - '0'; 264 else if('A' <= c && c <= 'Z') 265 n = c - 'A' + 10; 266 else if('a' <= c && c <= 'z') 267 n = c - 'a' + 10; 268 else 269 break; 270 if(radix <= n) 271 break; 272 number = number * radix + n; 273 } 274 if(z == s) 275 { 276 number = d_number.nan; 277 goto Lret; 278 } 279 if(sign < 0) 280 number = -number; 281 282 version(none) // ECMA says to silently ignore trailing characters 283 { 284 while(z - &string[0] < string.length) 285 { 286 if(!isStrWhiteSpaceChar(*z)) 287 { 288 number = d_number.nan; 289 goto Lret; 290 } 291 z++; 292 } 293 } 294 295 Lret: 296 ret.putVnumber(number); 297 return null; 298 } 299 300 /* ====================== Dglobal_parseFloat ================ */ 301 302 void* Dglobal_parseFloat(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 303 { 304 // ECMA 15.1.2.3 305 d_number n; 306 size_t endidx; 307 308 d_string string = arg0string(arglist); 309 n = StringNumericLiteral(string, endidx, 1); 310 311 ret.putVnumber(n); 312 return null; 313 } 314 315 /* ====================== Dglobal_escape ================ */ 316 317 int ISURIALNUM(dchar c) 318 { 319 return (c >= 'a' && c <= 'z') || 320 (c >= 'A' && c <= 'Z') || 321 (c >= '0' && c <= '9'); 322 } 323 324 tchar TOHEX[16 + 1] = "0123456789ABCDEF"; 325 326 void* Dglobal_escape(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 327 { 328 // ECMA 15.1.2.4 329 d_string s; 330 uint escapes; 331 uint unicodes; 332 size_t slen; 333 334 s = arg0string(arglist); 335 escapes = 0; 336 unicodes = 0; 337 foreach(dchar c; s) 338 { 339 slen++; 340 if(c >= 0x100) 341 unicodes++; 342 else 343 if(c == 0 || c >= 0x80 || (!ISURIALNUM(c) && std..string.indexOf("*@-_+./", c) == -1)) 344 escapes++; 345 } 346 if((escapes + unicodes) == 0) 347 { 348 ret.putVstring(assumeUnique(s)); 349 return null; 350 } 351 else 352 { 353 //writefln("s.length = %d, escapes = %d, unicodes = %d", s.length, escapes, unicodes); 354 char[] R = new char[slen + escapes * 2 + unicodes * 5]; 355 char* r = R.ptr; 356 foreach(dchar c; s) 357 { 358 if(c >= 0x100) 359 { 360 r[0] = '%'; 361 r[1] = 'u'; 362 r[2] = TOHEX[(c >> 12) & 15]; 363 r[3] = TOHEX[(c >> 8) & 15]; 364 r[4] = TOHEX[(c >> 4) & 15]; 365 r[5] = TOHEX[c & 15]; 366 r += 6; 367 } 368 else if(c == 0 || c >= 0x80 || (!ISURIALNUM(c) && std..string.indexOf("*@-_+./", c) == -1)) 369 { 370 r[0] = '%'; 371 r[1] = TOHEX[c >> 4]; 372 r[2] = TOHEX[c & 15]; 373 r += 3; 374 } 375 else 376 { 377 r[0] = cast(tchar)c; 378 r++; 379 } 380 } 381 assert(r - R.ptr == R.length); 382 ret.putVstring(assumeUnique(R)); 383 return null; 384 } 385 } 386 387 /* ====================== Dglobal_unescape ================ */ 388 389 void* Dglobal_unescape(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 390 { 391 // ECMA 15.1.2.5 392 d_string s; 393 d_string R; 394 395 s = arg0string(arglist); 396 //writefln("Dglobal.unescape(s = '%s')", s); 397 for(size_t k = 0; k < s.length; k++) 398 { 399 tchar c = s[k]; 400 401 if(c == '%') 402 { 403 if(k + 6 <= s.length && s[k + 1] == 'u') 404 { 405 uint u; 406 407 u = 0; 408 for(int i = 2;; i++) 409 { 410 uint x; 411 412 if(i == 6) 413 { 414 dmdscript.utf.encode(R, cast(dchar)u); 415 k += 5; 416 goto L1; 417 } 418 x = s[k + i]; 419 if('0' <= x && x <= '9') 420 x = x - '0'; 421 else if('A' <= x && x <= 'F') 422 x = x - 'A' + 10; 423 else if('a' <= x && x <= 'f') 424 x = x - 'a' + 10; 425 else 426 break; 427 u = (u << 4) + x; 428 } 429 } 430 else if(k + 3 <= s.length) 431 { 432 uint u; 433 434 u = 0; 435 for(int i = 1;; i++) 436 { 437 uint x; 438 439 if(i == 3) 440 { 441 dmdscript.utf.encode(R, cast(dchar)u); 442 k += 2; 443 goto L1; 444 } 445 x = s[k + i]; 446 if('0' <= x && x <= '9') 447 x = x - '0'; 448 else if('A' <= x && x <= 'F') 449 x = x - 'A' + 10; 450 else if('a' <= x && x <= 'f') 451 x = x - 'a' + 10; 452 else 453 break; 454 u = (u << 4) + x; 455 } 456 } 457 } 458 R ~= c; 459 L1: 460 ; 461 } 462 463 ret.putVstring(R); 464 return null; 465 } 466 467 /* ====================== Dglobal_isNaN ================ */ 468 469 void* Dglobal_isNaN(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 470 { 471 // ECMA 15.1.2.6 472 Value* v; 473 d_number n; 474 d_boolean b; 475 476 if(arglist.length) 477 v = &arglist[0]; 478 else 479 v = &vundefined; 480 n = v.toNumber(); 481 b = isnan(n) ? true : false; 482 ret.putVboolean(b); 483 return null; 484 } 485 486 /* ====================== Dglobal_isFinite ================ */ 487 488 void* Dglobal_isFinite(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 489 { 490 // ECMA 15.1.2.7 491 Value* v; 492 d_number n; 493 d_boolean b; 494 495 if(arglist.length) 496 v = &arglist[0]; 497 else 498 v = &vundefined; 499 n = v.toNumber(); 500 b = isfinite(n) ? true : false; 501 ret.putVboolean(b); 502 return null; 503 } 504 505 /* ====================== Dglobal_ URI Functions ================ */ 506 507 void* URI_error(d_string s) 508 { 509 Dobject o = new urierror.D0(s ~ "() failure"); 510 Value* v = new Value; 511 v.putVobject(o); 512 return v; 513 } 514 515 void* Dglobal_decodeURI(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 516 { 517 // ECMA v3 15.1.3.1 518 d_string s; 519 520 s = arg0string(arglist); 521 try 522 { 523 s = std.uri.decode(s); 524 } 525 catch(URIException u) 526 { 527 ret.putVundefined(); 528 return URI_error(TEXT_decodeURI); 529 } 530 ret.putVstring(s); 531 return null; 532 } 533 534 void* Dglobal_decodeURIComponent(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 535 { 536 // ECMA v3 15.1.3.2 537 d_string s; 538 539 s = arg0string(arglist); 540 try 541 { 542 s = std.uri.decodeComponent(s); 543 } 544 catch(URIException u) 545 { 546 ret.putVundefined(); 547 return URI_error(TEXT_decodeURIComponent); 548 } 549 ret.putVstring(s); 550 return null; 551 } 552 553 void* Dglobal_encodeURI(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 554 { 555 // ECMA v3 15.1.3.3 556 d_string s; 557 558 s = arg0string(arglist); 559 try 560 { 561 s = std.uri.encode(s); 562 } 563 catch(URIException u) 564 { 565 ret.putVundefined(); 566 return URI_error(TEXT_encodeURI); 567 } 568 ret.putVstring(s); 569 return null; 570 } 571 572 void* Dglobal_encodeURIComponent(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 573 { 574 // ECMA v3 15.1.3.4 575 d_string s; 576 577 s = arg0string(arglist); 578 try 579 { 580 s = std.uri.encodeComponent(s); 581 } 582 catch(URIException u) 583 { 584 ret.putVundefined(); 585 return URI_error(TEXT_encodeURIComponent); 586 } 587 ret.putVstring(s); 588 return null; 589 } 590 591 /* ====================== Dglobal_print ================ */ 592 593 static void dglobal_print(CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 594 { 595 // Our own extension 596 if(arglist.length) 597 { 598 uint i; 599 600 for(i = 0; i < arglist.length; i++) 601 { 602 d_string s = arglist[i].toString(); 603 604 writef("%s", s); 605 } 606 } 607 608 ret.putVundefined(); 609 } 610 611 void* Dglobal_print(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 612 { 613 // Our own extension 614 dglobal_print(cc, othis, ret, arglist); 615 return null; 616 } 617 618 /* ====================== Dglobal_println ================ */ 619 620 void* Dglobal_println(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 621 { 622 // Our own extension 623 dglobal_print(cc, othis, ret, arglist); 624 writef("\n"); 625 return null; 626 } 627 628 /* ====================== Dglobal_readln ================ */ 629 630 void* Dglobal_readln(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 631 { 632 // Our own extension 633 dchar c; 634 d_string s; 635 636 for(;; ) 637 { 638 version(linux) 639 { 640 c = std.c.stdio.getchar(); 641 if(c == EOF) 642 break; 643 } 644 else version(Windows) 645 { 646 c = std.c.stdio.getchar(); 647 if(c == EOF) 648 break; 649 } 650 else version(OSX) 651 { 652 c = std.c.stdio.getchar(); 653 if(c == EOF) 654 break; 655 } 656 else version(FreeBSD) 657 { 658 c = std.c.stdio.getchar(); 659 if(c == EOF) 660 break; 661 } 662 else 663 { 664 static assert(0); 665 } 666 if(c == '\n') 667 break; 668 dmdscript.utf.encode(s, c); 669 } 670 ret.putVstring(s); 671 return null; 672 } 673 674 /* ====================== Dglobal_getenv ================ */ 675 676 void* Dglobal_getenv(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 677 { 678 // Our own extension 679 ret.putVundefined(); 680 if(arglist.length) 681 { 682 d_string s = arglist[0].toString(); 683 char* p = getenv(std..string.toStringz(s)); 684 if(p) 685 ret.putVstring(p[0 .. strlen(p)].idup); 686 else 687 ret.putVnull(); 688 } 689 return null; 690 } 691 692 693 /* ====================== Dglobal_ScriptEngine ================ */ 694 695 void* Dglobal_ScriptEngine(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 696 { 697 ret.putVstring(TEXT_DMDScript); 698 return null; 699 } 700 701 void* Dglobal_ScriptEngineBuildVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 702 { 703 ret.putVnumber(BUILD_VERSION); 704 return null; 705 } 706 707 void* Dglobal_ScriptEngineMajorVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 708 { 709 ret.putVnumber(MAJOR_VERSION); 710 return null; 711 } 712 713 void* Dglobal_ScriptEngineMinorVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 714 { 715 ret.putVnumber(MINOR_VERSION); 716 return null; 717 } 718 719 /* ====================== Dglobal =========================== */ 720 721 class Dglobal : Dobject 722 { 723 this(tchar[][] argv) 724 { 725 super(Dobject.getPrototype()); // Dglobal.prototype is implementation-dependent 726 727 //writef("Dglobal.Dglobal(%x)\n", this); 728 729 Dobject f = Dfunction.getPrototype(); 730 731 classname = TEXT_global; 732 733 // ECMA 15.1 734 // Add in built-in objects which have attribute { DontEnum } 735 736 // Value properties 737 738 Put(TEXT_NaN, d_number.nan, DontEnum | DontDelete); 739 Put(TEXT_Infinity, d_number.infinity, DontEnum| DontDelete); 740 Put(TEXT_undefined, &vundefined, DontEnum| DontDelete); 741 static enum NativeFunctionData nfd[] = 742 [ 743 // Function properties 744 { TEXT_eval, &Dglobal_eval, 1 }, 745 { TEXT_parseInt, &Dglobal_parseInt, 2 }, 746 { TEXT_parseFloat, &Dglobal_parseFloat, 1 }, 747 { TEXT_escape, &Dglobal_escape, 1 }, 748 { TEXT_unescape, &Dglobal_unescape, 1 }, 749 { TEXT_isNaN, &Dglobal_isNaN, 1 }, 750 { TEXT_isFinite, &Dglobal_isFinite, 1 }, 751 { TEXT_decodeURI, &Dglobal_decodeURI, 1 }, 752 { TEXT_decodeURIComponent, &Dglobal_decodeURIComponent, 1 }, 753 { TEXT_encodeURI, &Dglobal_encodeURI, 1 }, 754 { TEXT_encodeURIComponent, &Dglobal_encodeURIComponent, 1 }, 755 756 // Dscript unique function properties 757 { TEXT_print, &Dglobal_print, 1 }, 758 { TEXT_println, &Dglobal_println, 1 }, 759 { TEXT_readln, &Dglobal_readln, 0 }, 760 { TEXT_getenv, &Dglobal_getenv, 1 }, 761 762 // Jscript compatible extensions 763 { TEXT_ScriptEngine, &Dglobal_ScriptEngine, 0 }, 764 { TEXT_ScriptEngineBuildVersion, &Dglobal_ScriptEngineBuildVersion, 0 }, 765 { TEXT_ScriptEngineMajorVersion, &Dglobal_ScriptEngineMajorVersion, 0 }, 766 { TEXT_ScriptEngineMinorVersion, &Dglobal_ScriptEngineMinorVersion, 0 }, 767 ]; 768 769 DnativeFunction.initialize(this, nfd, DontEnum); 770 771 // Now handled by AssertExp() 772 // Put(TEXT_assert, Dglobal_assert(), DontEnum); 773 774 // Constructor properties 775 776 Put(TEXT_Object, Dobject_constructor, DontEnum); 777 Put(TEXT_Function, Dfunction_constructor, DontEnum); 778 Put(TEXT_Array, Darray_constructor, DontEnum); 779 Put(TEXT_String, Dstring_constructor, DontEnum); 780 Put(TEXT_Boolean, Dboolean_constructor, DontEnum); 781 Put(TEXT_Number, Dnumber_constructor, DontEnum); 782 Put(TEXT_Date, Ddate_constructor, DontEnum); 783 Put(TEXT_RegExp, Dregexp_constructor, DontEnum); 784 Put(TEXT_Error, Derror_constructor, DontEnum); 785 786 foreach(d_string key, Dfunction ctor; ctorTable) 787 { 788 Put(key, ctor, DontEnum); 789 } 790 791 // Other properties 792 793 assert(Dmath_object); 794 Put(TEXT_Math, Dmath_object, DontEnum); 795 796 // Build an "arguments" property out of argv[], 797 // and add it to the global object. 798 Darray arguments; 799 800 arguments = new Darray(); 801 Put(TEXT_arguments, arguments, DontDelete); 802 arguments.length.putVnumber(argv.length); 803 for(int i = 0; i < argv.length; i++) 804 { 805 arguments.Put(i, argv[i].idup, DontEnum); 806 } 807 arguments.Put(TEXT_callee, &vnull, DontEnum); 808 } 809 }