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 core.stdc.stdlib; 22 import core.stdc..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 immutable 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 static import std.utf; 182 183 // ECMA 15.1.2.2 184 Value* v2; 185 immutable(char) * s; 186 immutable(char) * z; 187 d_int32 radix; 188 int sign = 1; 189 d_number number; 190 size_t i; 191 d_string string; 192 193 string = arg0string(arglist); 194 195 //writefln("Dglobal_parseInt('%s')", string); 196 197 while(i < string.length) 198 { 199 size_t idx = i; 200 dchar c = std.utf.decode(string, idx); 201 if(!isStrWhiteSpaceChar(c)) 202 break; 203 i = idx; 204 } 205 s = string.ptr + i; 206 i = string.length - i; 207 208 if(i) 209 { 210 if(*s == '-') 211 { 212 sign = -1; 213 s++; 214 i--; 215 } 216 else if(*s == '+') 217 { 218 s++; 219 i--; 220 } 221 } 222 223 radix = 0; 224 if(arglist.length >= 2) 225 { 226 v2 = &arglist[1]; 227 radix = v2.toInt32(); 228 } 229 230 if(radix) 231 { 232 if(radix < 2 || radix > 36) 233 { 234 number = d_number.nan; 235 goto Lret; 236 } 237 if(radix == 16 && i >= 2 && *s == '0' && 238 (s[1] == 'x' || s[1] == 'X')) 239 { 240 s += 2; 241 i -= 2; 242 } 243 } 244 else if(i >= 1 && *s != '0') 245 { 246 radix = 10; 247 } 248 else if(i >= 2 && (s[1] == 'x' || s[1] == 'X')) 249 { 250 radix = 16; 251 s += 2; 252 i -= 2; 253 } 254 else 255 radix = 8; 256 257 number = 0; 258 for(z = s; i; z++, i--) 259 { 260 d_int32 n; 261 tchar c; 262 263 c = *z; 264 if('0' <= c && c <= '9') 265 n = c - '0'; 266 else if('A' <= c && c <= 'Z') 267 n = c - 'A' + 10; 268 else if('a' <= c && c <= 'z') 269 n = c - 'a' + 10; 270 else 271 break; 272 if(radix <= n) 273 break; 274 number = number * radix + n; 275 } 276 if(z == s) 277 { 278 number = d_number.nan; 279 goto Lret; 280 } 281 if(sign < 0) 282 number = -number; 283 284 version(none) // ECMA says to silently ignore trailing characters 285 { 286 while(z - &string[0] < string.length) 287 { 288 if(!isStrWhiteSpaceChar(*z)) 289 { 290 number = d_number.nan; 291 goto Lret; 292 } 293 z++; 294 } 295 } 296 297 Lret: 298 ret.putVnumber(number); 299 return null; 300 } 301 302 /* ====================== Dglobal_parseFloat ================ */ 303 304 void* Dglobal_parseFloat(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 305 { 306 // ECMA 15.1.2.3 307 d_number n; 308 size_t endidx; 309 310 d_string string = arg0string(arglist); 311 n = StringNumericLiteral(string, endidx, 1); 312 313 ret.putVnumber(n); 314 return null; 315 } 316 317 /* ====================== Dglobal_escape ================ */ 318 319 int ISURIALNUM(dchar c) 320 { 321 return (c >= 'a' && c <= 'z') || 322 (c >= 'A' && c <= 'Z') || 323 (c >= '0' && c <= '9'); 324 } 325 326 immutable tchar[16 + 1] TOHEX = "0123456789ABCDEF"; 327 328 void* Dglobal_escape(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 329 { 330 import std..string : indexOf; 331 332 // ECMA 15.1.2.4 333 d_string s; 334 uint escapes; 335 uint unicodes; 336 size_t slen; 337 338 s = arg0string(arglist); 339 escapes = 0; 340 unicodes = 0; 341 foreach(dchar c; s) 342 { 343 slen++; 344 if(c >= 0x100) 345 unicodes++; 346 else 347 if(c == 0 || c >= 0x80 || (!ISURIALNUM(c) && indexOf("*@-_+./", c) == -1)) 348 escapes++; 349 } 350 if((escapes + unicodes) == 0) 351 { 352 ret.putVstring(assumeUnique(s)); 353 return null; 354 } 355 else 356 { 357 //writefln("s.length = %d, escapes = %d, unicodes = %d", s.length, escapes, unicodes); 358 char[] R = new char[slen + escapes * 2 + unicodes * 5]; 359 char* r = R.ptr; 360 foreach(dchar c; s) 361 { 362 if(c >= 0x100) 363 { 364 r[0] = '%'; 365 r[1] = 'u'; 366 r[2] = TOHEX[(c >> 12) & 15]; 367 r[3] = TOHEX[(c >> 8) & 15]; 368 r[4] = TOHEX[(c >> 4) & 15]; 369 r[5] = TOHEX[c & 15]; 370 r += 6; 371 } 372 else if(c == 0 || c >= 0x80 || (!ISURIALNUM(c) && indexOf("*@-_+./", c) == -1)) 373 { 374 r[0] = '%'; 375 r[1] = TOHEX[c >> 4]; 376 r[2] = TOHEX[c & 15]; 377 r += 3; 378 } 379 else 380 { 381 r[0] = cast(tchar)c; 382 r++; 383 } 384 } 385 assert(r - R.ptr == R.length); 386 ret.putVstring(assumeUnique(R)); 387 return null; 388 } 389 } 390 391 /* ====================== Dglobal_unescape ================ */ 392 393 void* Dglobal_unescape(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 394 { 395 static import dmdscript.utf; 396 397 // ECMA 15.1.2.5 398 d_string s; 399 d_string R; 400 401 s = arg0string(arglist); 402 //writefln("Dglobal.unescape(s = '%s')", s); 403 for(size_t k = 0; k < s.length; k++) 404 { 405 tchar c = s[k]; 406 407 if(c == '%') 408 { 409 if(k + 6 <= s.length && s[k + 1] == 'u') 410 { 411 uint u; 412 413 u = 0; 414 for(int i = 2;; i++) 415 { 416 uint x; 417 418 if(i == 6) 419 { 420 dmdscript.utf.encode(R, cast(dchar)u); 421 k += 5; 422 goto L1; 423 } 424 x = s[k + i]; 425 if('0' <= x && x <= '9') 426 x = x - '0'; 427 else if('A' <= x && x <= 'F') 428 x = x - 'A' + 10; 429 else if('a' <= x && x <= 'f') 430 x = x - 'a' + 10; 431 else 432 break; 433 u = (u << 4) + x; 434 } 435 } 436 else if(k + 3 <= s.length) 437 { 438 uint u; 439 440 u = 0; 441 for(int i = 1;; i++) 442 { 443 uint x; 444 445 if(i == 3) 446 { 447 dmdscript.utf.encode(R, cast(dchar)u); 448 k += 2; 449 goto L1; 450 } 451 x = s[k + i]; 452 if('0' <= x && x <= '9') 453 x = x - '0'; 454 else if('A' <= x && x <= 'F') 455 x = x - 'A' + 10; 456 else if('a' <= x && x <= 'f') 457 x = x - 'a' + 10; 458 else 459 break; 460 u = (u << 4) + x; 461 } 462 } 463 } 464 R ~= c; 465 L1: 466 ; 467 } 468 469 ret.putVstring(R); 470 return null; 471 } 472 473 /* ====================== Dglobal_isNaN ================ */ 474 475 void* Dglobal_isNaN(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 476 { 477 // ECMA 15.1.2.6 478 Value* v; 479 d_number n; 480 d_boolean b; 481 482 if(arglist.length) 483 v = &arglist[0]; 484 else 485 v = &vundefined; 486 n = v.toNumber(); 487 b = isNaN(n) ? true : false; 488 ret.putVboolean(b); 489 return null; 490 } 491 492 /* ====================== Dglobal_isFinite ================ */ 493 494 void* Dglobal_isFinite(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 495 { 496 // ECMA 15.1.2.7 497 Value* v; 498 d_number n; 499 d_boolean b; 500 501 if(arglist.length) 502 v = &arglist[0]; 503 else 504 v = &vundefined; 505 n = v.toNumber(); 506 b = isFinite(n) ? true : false; 507 ret.putVboolean(b); 508 return null; 509 } 510 511 /* ====================== Dglobal_ URI Functions ================ */ 512 513 void* URI_error(d_string s) 514 { 515 Dobject o = new urierror.D0(s ~ "() failure"); 516 Value* v = new Value; 517 v.putVobject(o); 518 return v; 519 } 520 521 void* Dglobal_decodeURI(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 522 { 523 // ECMA v3 15.1.3.1 524 d_string s; 525 526 s = arg0string(arglist); 527 try 528 { 529 s = std.uri.decode(s); 530 } 531 catch(URIException u) 532 { 533 ret.putVundefined(); 534 return URI_error(TEXT_decodeURI); 535 } 536 ret.putVstring(s); 537 return null; 538 } 539 540 void* Dglobal_decodeURIComponent(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 541 { 542 // ECMA v3 15.1.3.2 543 d_string s; 544 545 s = arg0string(arglist); 546 try 547 { 548 s = std.uri.decodeComponent(s); 549 } 550 catch(URIException u) 551 { 552 ret.putVundefined(); 553 return URI_error(TEXT_decodeURIComponent); 554 } 555 ret.putVstring(s); 556 return null; 557 } 558 559 void* Dglobal_encodeURI(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 560 { 561 // ECMA v3 15.1.3.3 562 d_string s; 563 564 s = arg0string(arglist); 565 try 566 { 567 s = std.uri.encode(s); 568 } 569 catch(URIException u) 570 { 571 ret.putVundefined(); 572 return URI_error(TEXT_encodeURI); 573 } 574 ret.putVstring(s); 575 return null; 576 } 577 578 void* Dglobal_encodeURIComponent(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 579 { 580 // ECMA v3 15.1.3.4 581 d_string s; 582 583 s = arg0string(arglist); 584 try 585 { 586 s = std.uri.encodeComponent(s); 587 } 588 catch(URIException u) 589 { 590 ret.putVundefined(); 591 return URI_error(TEXT_encodeURIComponent); 592 } 593 ret.putVstring(s); 594 return null; 595 } 596 597 /* ====================== Dglobal_print ================ */ 598 599 static void dglobal_print(CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 600 { 601 // Our own extension 602 if(arglist.length) 603 { 604 uint i; 605 606 for(i = 0; i < arglist.length; i++) 607 { 608 d_string s = arglist[i].toString(); 609 610 writef("%s", s); 611 } 612 } 613 614 ret.putVundefined(); 615 } 616 617 void* Dglobal_print(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 618 { 619 // Our own extension 620 dglobal_print(cc, othis, ret, arglist); 621 return null; 622 } 623 624 /* ====================== Dglobal_println ================ */ 625 626 void* Dglobal_println(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 627 { 628 // Our own extension 629 dglobal_print(cc, othis, ret, arglist); 630 writef("\n"); 631 return null; 632 } 633 634 /* ====================== Dglobal_readln ================ */ 635 636 void* Dglobal_readln(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 637 { 638 static import dmdscript.utf; 639 640 // Our own extension 641 dchar c; 642 d_string s; 643 644 for(;; ) 645 { 646 version(linux) 647 { 648 c = core.stdc.stdio.getchar(); 649 if(c == EOF) 650 break; 651 } 652 else version(Windows) 653 { 654 c = core.stdc.stdio.getchar(); 655 if(c == EOF) 656 break; 657 } 658 else version(OSX) 659 { 660 c = core.stdc.stdio.getchar(); 661 if(c == EOF) 662 break; 663 } 664 else version(FreeBSD) 665 { 666 c = core.stdc.stdio.getchar(); 667 if(c == EOF) 668 break; 669 } 670 else 671 { 672 static assert(0); 673 } 674 if(c == '\n') 675 break; 676 dmdscript.utf.encode(s, c); 677 } 678 ret.putVstring(s); 679 return null; 680 } 681 682 /* ====================== Dglobal_getenv ================ */ 683 684 void* Dglobal_getenv(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 685 { 686 import std..string : toStringz; 687 688 // Our own extension 689 ret.putVundefined(); 690 if(arglist.length) 691 { 692 d_string s = arglist[0].toString(); 693 char* p = getenv(toStringz(s)); 694 if(p) 695 ret.putVstring(p[0 .. strlen(p)].idup); 696 else 697 ret.putVnull(); 698 } 699 return null; 700 } 701 702 703 /* ====================== Dglobal_ScriptEngine ================ */ 704 705 void* Dglobal_ScriptEngine(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 706 { 707 ret.putVstring(TEXT_DMDScript); 708 return null; 709 } 710 711 void* Dglobal_ScriptEngineBuildVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 712 { 713 ret.putVnumber(BUILD_VERSION); 714 return null; 715 } 716 717 void* Dglobal_ScriptEngineMajorVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 718 { 719 ret.putVnumber(MAJOR_VERSION); 720 return null; 721 } 722 723 void* Dglobal_ScriptEngineMinorVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 724 { 725 ret.putVnumber(MINOR_VERSION); 726 return null; 727 } 728 729 /* ====================== Dglobal =========================== */ 730 731 class Dglobal : Dobject 732 { 733 this(tchar[][] argv) 734 { 735 super(Dobject.getPrototype()); // Dglobal.prototype is implementation-dependent 736 737 //writef("Dglobal.Dglobal(%x)\n", this); 738 739 Dobject f = Dfunction.getPrototype(); 740 741 classname = TEXT_global; 742 743 // ECMA 15.1 744 // Add in built-in objects which have attribute { DontEnum } 745 746 // Value properties 747 748 Put(TEXT_NaN, d_number.nan, DontEnum | DontDelete); 749 Put(TEXT_Infinity, d_number.infinity, DontEnum| DontDelete); 750 Put(TEXT_undefined, &vundefined, DontEnum| DontDelete); 751 static enum NativeFunctionData[] nfd = 752 [ 753 // Function properties 754 { TEXT_eval, &Dglobal_eval, 1 }, 755 { TEXT_parseInt, &Dglobal_parseInt, 2 }, 756 { TEXT_parseFloat, &Dglobal_parseFloat, 1 }, 757 { TEXT_escape, &Dglobal_escape, 1 }, 758 { TEXT_unescape, &Dglobal_unescape, 1 }, 759 { TEXT_isNaN, &Dglobal_isNaN, 1 }, 760 { TEXT_isFinite, &Dglobal_isFinite, 1 }, 761 { TEXT_decodeURI, &Dglobal_decodeURI, 1 }, 762 { TEXT_decodeURIComponent, &Dglobal_decodeURIComponent, 1 }, 763 { TEXT_encodeURI, &Dglobal_encodeURI, 1 }, 764 { TEXT_encodeURIComponent, &Dglobal_encodeURIComponent, 1 }, 765 766 // Dscript unique function properties 767 { TEXT_print, &Dglobal_print, 1 }, 768 { TEXT_println, &Dglobal_println, 1 }, 769 { TEXT_readln, &Dglobal_readln, 0 }, 770 { TEXT_getenv, &Dglobal_getenv, 1 }, 771 772 // Jscript compatible extensions 773 { TEXT_ScriptEngine, &Dglobal_ScriptEngine, 0 }, 774 { TEXT_ScriptEngineBuildVersion, &Dglobal_ScriptEngineBuildVersion, 0 }, 775 { TEXT_ScriptEngineMajorVersion, &Dglobal_ScriptEngineMajorVersion, 0 }, 776 { TEXT_ScriptEngineMinorVersion, &Dglobal_ScriptEngineMinorVersion, 0 }, 777 ]; 778 779 DnativeFunction.initialize(this, nfd, DontEnum); 780 781 // Now handled by AssertExp() 782 // Put(TEXT_assert, Dglobal_assert(), DontEnum); 783 784 // Constructor properties 785 786 Put(TEXT_Object, Dobject_constructor, DontEnum); 787 Put(TEXT_Function, Dfunction_constructor, DontEnum); 788 Put(TEXT_Array, Darray_constructor, DontEnum); 789 Put(TEXT_String, Dstring_constructor, DontEnum); 790 Put(TEXT_Boolean, Dboolean_constructor, DontEnum); 791 Put(TEXT_Number, Dnumber_constructor, DontEnum); 792 Put(TEXT_Date, Ddate_constructor, DontEnum); 793 Put(TEXT_RegExp, Dregexp_constructor, DontEnum); 794 Put(TEXT_Error, Derror_constructor, DontEnum); 795 796 foreach(d_string key, Dfunction ctor; ctorTable) 797 { 798 Put(key, ctor, DontEnum); 799 } 800 801 // Other properties 802 803 assert(Dmath_object); 804 Put(TEXT_Math, Dmath_object, DontEnum); 805 806 // Build an "arguments" property out of argv[], 807 // and add it to the global object. 808 Darray arguments; 809 810 arguments = new Darray(); 811 Put(TEXT_arguments, arguments, DontDelete); 812 arguments.length.putVnumber(argv.length); 813 for(int i = 0; i < argv.length; i++) 814 { 815 arguments.Put(i, argv[i].idup, DontEnum); 816 } 817 arguments.Put(TEXT_callee, &vnull, DontEnum); 818 } 819 }