1# 2# want to use the value in a context which 3# prefers an object, so coerce schizo vals 4# to object versions 5# 6coerceToObj(ex: ref Exec, v: ref Val): ref Val 7{ 8 o: ref Obj; 9 10 case v.ty{ 11 TBool => 12 o = mkobj(ex.boolproto, "Boolean"); 13 o.val = v; 14 TStr => 15 o = mkobj(ex.strproto, "String"); 16 o.val = v; 17 valinstant(o, DontEnum|DontDelete|ReadOnly, "length", numval(real len v.str)); 18 TNum => 19 o = mkobj(ex.numproto, "Number"); 20 o.val = v; 21 TRegExp => 22 o = mkobj(ex.regexpproto, "RegExp"); 23 o.val = v; 24 valinstant(o, DontEnum|DontDelete|ReadOnly, "length", numval(real len v.rev.p)); 25 valinstant(o, DontEnum|DontDelete|ReadOnly, "source", strval(v.rev.p)); 26 valinstant(o, DontEnum|DontDelete|ReadOnly, "global", strhas(v.rev.f, 'g')); 27 valinstant(o, DontEnum|DontDelete|ReadOnly, "ignoreCase", strhas(v.rev.f, 'i')); 28 valinstant(o, DontEnum|DontDelete|ReadOnly, "multiline", strhas(v.rev.f, 'm')); 29 valinstant(o, DontEnum|DontDelete, "lastIndex", numval(real v.rev.i)); 30 * => 31 return v; 32 } 33 return objval(o); 34} 35 36coerceToVal(v: ref Val): ref Val 37{ 38 if(v.ty != TObj) 39 return v; 40 o := v.obj; 41 if(o.host != nil && o.host != me 42 || o.class != "String" 43 || o.class != "Number" 44 || o.class != "Boolean") 45 return v; 46 return o.val; 47} 48 49isstrobj(o: ref Obj): int 50{ 51 return (o.host == nil || o.host == me) && o.class == "String"; 52} 53 54isnumobj(o: ref Obj): int 55{ 56 return (o.host == nil || o.host == me) && o.class == "Number"; 57} 58 59isboolobj(o: ref Obj): int 60{ 61 return (o.host == nil || o.host == me) && o.class == "Boolean"; 62} 63 64isdateobj(o: ref Obj): int 65{ 66 return (o.host == nil || o.host == me) && o.class == "Date"; 67} 68 69isregexpobj(o: ref Obj): int 70{ 71 return (o.host == nil || o.host == me) && o.class == "RegExp"; 72} 73 74isfuncobj(o: ref Obj): int 75{ 76 return (o.host == nil || o.host == me) && o.class == "Function"; 77} 78 79isarray(o: ref Obj): int 80{ 81# return (o.host == nil || o.host == me) && o.class == "Array"; 82 # relax the host test 83 # so that hosts can intercept Array operations and defer 84 # unhandled ops to the builtin 85 return o.class == "Array"; 86} 87 88iserr(o: ref Obj): int 89{ 90 return (o.host == nil || o.host == me) && o.class == "Error"; 91} 92 93isactobj(o: ref Obj): int 94{ 95 return o.host == nil && o.class == "Activation"; 96} 97 98isnull(v: ref Val): int 99{ 100 return v == null || v == nil; 101} 102 103isundefined(v: ref Val): int 104{ 105 return v == undefined; 106} 107 108isstr(v: ref Val): int 109{ 110 return v.ty == TStr; 111} 112 113isnum(v: ref Val): int 114{ 115 return v.ty == TNum; 116} 117 118isbool(v: ref Val): int 119{ 120 return v.ty == TBool; 121} 122 123isobj(v: ref Val): int 124{ 125 return v.ty == TObj; 126} 127 128isregexp(v: ref Val): int 129{ 130 return v.ty == TRegExp || v.ty == TObj && isregexpobj(v.obj); 131} 132 133# 134# retrieve the object field if it's valid 135# 136getobj(v: ref Val): ref Obj 137{ 138 if(v.ty == TObj) 139 return v.obj; 140 return nil; 141} 142 143isprimval(v: ref Val): int 144{ 145 return v.ty != TObj; 146} 147 148pushscope(ex: ref Exec, o: ref Obj) 149{ 150 ex.scopechain = o :: ex.scopechain; 151} 152 153popscope(ex: ref Exec) 154{ 155 ex.scopechain = tl ex.scopechain; 156} 157 158runtime(ex: ref Exec, o: ref Obj, s: string) 159{ 160 ex.error = s; 161 if(o == nil) 162 ex.errval = undefined; 163 else 164 ex.errval = objval(o); 165 if(debug['r']){ 166 print("ecmascript runtime error: %s\n", s); 167 if(""[5] == -1); # abort 168 } 169 raise "throw"; 170 exit; # never reached 171} 172 173mkobj(proto: ref Obj, class: string): ref Obj 174{ 175 if(class == nil) 176 class = "Object"; 177 return ref Obj(nil, proto, nil, nil, nil, class, nil, nil); 178} 179 180valcheck(ex: ref Exec, v: ref Val, hint: int) 181{ 182 if(v == nil 183 || v.ty < 0 184 || v.ty >= NoHint 185 || v.ty == TBool && v != true && v != false 186 || v.ty == TObj && v.obj == nil 187 || hint != NoHint && v.ty != hint) 188 runtime(ex, RangeError, "bad value generated by host object"); 189} 190 191# builtin methods for properties 192esget(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val 193{ 194 for( ; o != nil; o = o.prototype){ 195 if(!force && o.host != nil && o.host != me){ 196 v := o.host->get(ex, o, prop); 197 valcheck(ex, v, NoHint); 198 return v; 199 } 200 201 for(i := 0; i < len o.props; i++) 202 if(o.props[i] != nil && o.props[i].name == prop) 203 return o.props[i].val.val; 204 force = 0; 205 } 206 return undefined; 207} 208 209esputind(o: ref Obj, prop: string): int 210{ 211 empty := -1; 212 props := o.props; 213 for(i := 0; i < len props; i++){ 214 if(props[i] == nil) 215 empty = i; 216 else if(props[i].name == prop) 217 return i; 218 } 219 if(empty != -1) 220 return empty; 221 222 props = array[i+1] of ref Prop; 223 props[:] = o.props; 224 o.props = props; 225 return i; 226} 227 228esput(ex: ref Exec, o: ref Obj, prop: string, v: ref Val, force: int) 229{ 230 ai: big; 231 232 if(!force && o.host != nil && o.host != me) 233 return o.host->put(ex, o, prop, v); 234 235 if(escanput(ex, o, prop, 0) != true) 236 return; 237 238 # 239 # should this test for prototype == ex.arrayproto? 240 # hard to say, but 15.4.5 "Properties of Array Instances" implies not 241 # 242 if(isarray(o)) 243 al := toUint32(ex, esget(ex, o, "length", 1)); 244 245 i := esputind(o, prop); 246 props := o.props; 247 if(props[i] != nil) 248 props[i].val.val = v; 249 else 250 props[i] = ref Prop(0, prop, ref RefVal(v)); 251 if(!isarray(o)) 252 return; 253 254 if(prop == "length"){ 255 nl := toUint32(ex, v); 256 for(ai = nl; ai < al; ai++) 257 esdelete(ex, o, string ai, 1); 258 props[i].val.val = numval(real nl); 259 }else{ 260 ai = big prop; 261 if(prop != string ai || ai < big 0 || ai >= 16rffffffff) 262 return; 263 i = esputind(o, "length"); 264 if(props[i] == nil) 265 fatal(ex, "bogus array esput"); 266 else if(toUint32(ex, props[i].val.val) <= ai) 267 props[i].val.val = numval(real(ai+big 1)); 268 } 269} 270 271escanput(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val 272{ 273 for( ; o != nil; o = o.prototype){ 274 if(!force && o.host != nil && o.host != me){ 275 v := o.host->canput(ex, o, prop); 276 valcheck(ex, v, TBool); 277 return v; 278 } 279 280 for(i := 0; i < len o.props; i++){ 281 if(o.props[i] != nil && o.props[i].name == prop){ 282 if(o.props[i].attr & ReadOnly) 283 return false; 284 else 285 return true; 286 } 287 } 288 289 force = 0; 290 } 291 return true; 292} 293 294eshasproperty(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val 295{ 296 for(; o != nil; o = o.prototype){ 297 if(!force && o.host != nil && o.host != me){ 298 v := o.host->hasproperty(ex, o, prop); 299 valcheck(ex, v, TBool); 300 return v; 301 } 302 for(i := 0; i < len o.props; i++) 303 if(o.props[i] != nil && o.props[i].name == prop) 304 return true; 305 } 306 return false; 307} 308 309eshasenumprop(o: ref Obj, prop: string): ref Val 310{ 311 for(i := 0; i < len o.props; i++) 312 if(o.props[i] != nil && o.props[i].name == prop){ 313 if(o.props[i].attr & DontEnum) 314 return false; 315 return true; 316 } 317 return false; 318} 319 320propshadowed(start, end: ref Obj, prop: string): int 321{ 322 for(o := start; o != end; o = o.prototype){ 323 if(o.host != nil && o.host != me) 324 return 0; 325 for(i := 0; i < len o.props; i++) 326 if(o.props[i] != nil && o.props[i].name == prop) 327 return 1; 328 } 329 return 0; 330} 331 332esdelete(ex: ref Exec, o: ref Obj, prop: string, force: int) 333{ 334 if(!force && o.host != nil && o.host != me) 335 return o.host->delete(ex, o, prop); 336 337 for(i := 0; i < len o.props; i++){ 338 if(o.props[i] != nil && o.props[i].name == prop){ 339 if(!(o.props[i].attr & DontDelete)) 340 o.props[i] = nil; 341 return; 342 } 343 } 344} 345 346esdeforder := array[] of {"valueOf", "toString"}; 347esdefaultval(ex: ref Exec, o: ref Obj, ty: int, force: int): ref Val 348{ 349 v: ref Val; 350 351 if(!force && o.host != nil && o.host != me){ 352 v = o.host->defaultval(ex, o, ty); 353 valcheck(ex, v, NoHint); 354 if(!isprimval(v)) 355 runtime(ex, TypeError, "host object returned an object to [[DefaultValue]]"); 356 return v; 357 } 358 359 hintstr := 0; 360 if(ty == TStr || ty == NoHint && isdateobj(o)) 361 hintstr = 1; 362 363 for(i := 0; i < 2; i++){ 364 v = esget(ex, o, esdeforder[hintstr ^ i], 0); 365 if(v != undefined && v.ty == TObj && v.obj.call != nil){ 366 r := escall(ex, v.obj, o, nil, 0); 367 v = nil; 368 if(!r.isref) 369 v = r.val; 370 if(v != nil && isprimval(v)) 371 return v; 372 } 373 } 374 runtime(ex, TypeError, "no default value"); 375 return nil; 376} 377 378esprimid(ex: ref Exec, s: string): ref Ref 379{ 380 for(sc := ex.scopechain; sc != nil; sc = tl sc){ 381 o := hd sc; 382 if(eshasproperty(ex, o, s, 0) == true) 383 return ref Ref(1, nil, o, s); 384 } 385 386 # 387 # the right place to add literals? 388 # 389 case s{ 390 "null" => 391 return ref Ref(0, null, nil, "null"); 392 "true" => 393 return ref Ref(0, true, nil, "true"); 394 "false" => 395 return ref Ref(0, false, nil, "false"); 396 } 397 return ref Ref(1, nil, nil, s); 398} 399 400bivar(ex: ref Exec, sc: list of ref Obj, s: string): ref Val 401{ 402 for(; sc != nil; sc = tl sc){ 403 o := hd sc; 404 if(eshasproperty(ex, o, s, 0) == true) 405 return esget(ex, o, s, 0); 406 } 407 return nil; 408} 409 410esconstruct(ex: ref Exec, func: ref Obj, args: array of ref Val): ref Obj 411{ 412 o: ref Obj; 413 414 if(func.construct == nil) 415 runtime(ex, TypeError, "new must be applied to a constructor object"); 416 if(func.host != nil) 417 o = func.host->construct(ex, func, args); 418 else{ 419 o = getobj(esget(ex, func, "prototype", 0)); 420 if(o == nil) 421 o = ex.objproto; 422 this := mkobj(o, "Object"); 423 o = getobj(getValue(ex, escall(ex, func, this, args, 0))); 424 425 # Divergence from ECMA-262 426 # 427 # observed that not all script-defined constructors return an object, 428 # the value of 'this' is assumed to be the value of the constructor 429 if (o == nil) 430 o = this; 431 } 432 if(o == nil) 433 runtime(ex, TypeError, func.val.str+" failed to generate an object"); 434 return o; 435} 436 437escall(ex: ref Exec, func, this: ref Obj, args: array of ref Val, eval: int): ref Ref 438{ 439 if(func.call == nil) 440 runtime(ex, TypeError, "can only call function objects"); 441 if(this == nil) 442 this = ex.global; 443 444 r: ref Ref = nil; 445 if(func.host != nil){ 446 r = func.host->call(ex, func, this, args, 0); 447 if(r.isref && r.name == nil) 448 runtime(ex, ReferenceError, "host call returned a bad reference"); 449 else if(!r.isref) 450 valcheck(ex, r.val, NoHint); 451 return r; 452 } 453 454 argobj := mkobj(ex.objproto, "Object"); 455 actobj := mkobj(nil, "Activation"); 456 457 oargs: ref RefVal = nil; 458 props := func.props; 459 empty := -1; 460 i := 0; 461 for(i = 0; i < len props; i++){ 462 if(props[i] == nil) 463 empty = i; 464 else if(props[i].name == "arguments"){ 465 oargs = props[i].val; 466 empty = i; 467 break; 468 } 469 } 470 if(i == len func.props){ 471 if(empty == -1){ 472 props = array[i+1] of ref Prop; 473 props[:] = func.props; 474 func.props = props; 475 empty = i; 476 } 477 props[empty] = ref Prop(DontDelete|DontEnum|ReadOnly, "arguments", nil); 478 } 479 props[empty].val = ref RefVal(objval(argobj)); 480 481 # 482 #see section 10.1.3 page 33 483 # if multiple params share the same name, the last one takes effect 484 # vars don't override params of the same name, or earlier parms defs 485 # 486 actobj.props = array[] of {ref Prop(DontDelete, "arguments", ref RefVal(objval(argobj)))}; 487 488 argobj.props = array[len args + 2] of { 489 ref Prop(DontEnum, "callee", ref RefVal(objval(func))), 490 ref Prop(DontEnum, "length", ref RefVal(numval(real len args))), 491 }; 492 493 # 494 # instantiate the arguments by name in the activation object 495 # and by number in the arguments object, aliased to the same RefVal. 496 # 497 params := func.call.params; 498 for(i = 0; i < len args; i++){ 499 rjv := ref RefVal(args[i]); 500 argobj.props[i+2] = ref Prop(DontEnum, string i, rjv); 501 if(i < len params) 502 fvarinstant(actobj, 1, DontDelete, params[i], rjv); 503 } 504 for(; i < len params; i++) 505 fvarinstant(actobj, 1, DontDelete, params[i], ref RefVal(undefined)); 506 507 # 508 # instantiate the local variables defined within the function 509 # 510 vars := func.call.code.vars; 511 for(i = 0; i < len vars; i++) 512 valinstant(actobj, DontDelete, vars[i].name, undefined); 513 514 # NOTE: the treatment of scopechain here is wrong if nested functions are 515 # permitted. ECMA-262 currently does not support nested functions (so we 516 # are ok for now) - but other flavours of Javascript do. 517 # Difficulties are introduced by multiple execution contexts. 518 # e.g. in web browsers, one frame can ref a func in 519 # another frame (each frame has a distinct execution context), but the func 520 # ids must bind as if in original lexical context 521 522 osc := ex.scopechain; 523 ex.this = this; 524 ex.scopechain = actobj :: osc; 525 (k, v, nil) := exec(ex, func.call.code); 526 ex.scopechain = osc; 527 528 # 529 # i can find nothing in the docs which defines 530 # the value of a function call 531 # this seems like a reasonable definition 532 # 533 if (k == CThrow) 534 raise "throw"; 535 if(!eval && k != CReturn || v == nil) 536 v = undefined; 537 r = valref(v); 538 539 props = func.props; 540 for(i = 0; i < len props; i++){ 541 if(props[i] != nil && props[i].name == "arguments"){ 542 if(oargs == nil) 543 props[i] = nil; 544 else 545 props[i].val = oargs; 546 break; 547 } 548 } 549 550 return r; 551} 552 553# 554# routines for instantiating variables 555# 556fvarinstant(o: ref Obj, force, attr: int, s: string, v: ref RefVal) 557{ 558 props := o.props; 559 empty := -1; 560 for(i := 0; i < len props; i++){ 561 if(props[i] == nil) 562 empty = i; 563 else if(props[i].name == s){ 564 if(force){ 565 props[i].attr = attr; 566 props[i].val = v; 567 } 568 return; 569 } 570 } 571 if(empty == -1){ 572 props = array[i+1] of ref Prop; 573 props[:] = o.props; 574 o.props = props; 575 empty = i; 576 } 577 props[empty] = ref Prop(attr, s, v); 578} 579 580varinstant(o: ref Obj, attr: int, s: string, v: ref RefVal) 581{ 582 fvarinstant(o, 0, attr, s, v); 583} 584 585valinstant(o: ref Obj, attr: int, s: string, v: ref Val) 586{ 587 fvarinstant(o, 0, attr, s, ref RefVal(v)); 588} 589 590# 591# instantiate global or val variables 592# note that only function variables are forced to be redefined; 593# all other variables have a undefined val.val field 594# 595globalinstant(o: ref Obj, vars: array of ref Prop) 596{ 597 for(i := 0; i < len vars; i++){ 598 force := vars[i].val.val != undefined; 599 fvarinstant(o, force, 0, vars[i].name, vars[i].val); 600 } 601} 602 603numval(r: real): ref Val 604{ 605 return ref Val(TNum, r, nil, nil, nil); 606} 607 608strval(s: string): ref Val 609{ 610 return ref Val(TStr, 0., s, nil, nil); 611} 612 613objval(o: ref Obj): ref Val 614{ 615 return ref Val(TObj, 0., nil, o, nil); 616} 617 618regexpval(p: string, f: string, i: int): ref Val 619{ 620 return ref Val(TRegExp, 0., nil, nil, ref REval(p, f, i)); 621} 622 623# 624# operations on refereneces 625# note the substitution of nil for an object 626# version of null, implied in the discussion of 627# Reference Types, since there isn't a null object 628# 629valref(v: ref Val): ref Ref 630{ 631 return ref Ref(0, v, nil, nil); 632} 633 634getBase(ex: ref Exec, r: ref Ref): ref Obj 635{ 636 if(!r.isref) 637 runtime(ex, ReferenceError, "not a reference"); 638 return r.base; 639} 640 641getPropertyName(ex: ref Exec, r: ref Ref): string 642{ 643 if(!r.isref) 644 runtime(ex, ReferenceError, "not a reference"); 645 return r.name; 646} 647 648getValue(ex: ref Exec, r: ref Ref): ref Val 649{ 650 if(!r.isref) 651 return r.val; 652 b := r.base; 653 if(b == nil) 654 runtime(ex, ReferenceError, "reference " + r.name + " is null"); 655 return esget(ex, b, r.name, 0); 656} 657 658putValue(ex: ref Exec, r: ref Ref, v: ref Val) 659{ 660 if(!r.isref) 661 runtime(ex, ReferenceError, "not a reference: " + r.name); 662 b := r.base; 663 if(b == nil) 664 b = ex.global; 665 esput(ex, b, r.name, v, 0); 666} 667 668# 669# conversion routines defined by the abstract machine 670# see section 9. 671# note that string, boolean, and number objects are 672# not automaically coerced to values, and vice versa. 673# 674toPrimitive(ex: ref Exec, v: ref Val, ty: int): ref Val 675{ 676 if(v.ty != TObj) 677 return v; 678 v = esdefaultval(ex, v.obj, ty, 0); 679 if(v.ty == TObj) 680 runtime(ex, TypeError, "toPrimitive returned an object"); 681 return v; 682} 683 684toBoolean(ex: ref Exec, v: ref Val): ref Val 685{ 686 case v.ty{ 687 TUndef or 688 TNull => 689 return false; 690 TBool => 691 return v; 692 TNum => 693 if(isnan(v.num)) 694 return false; 695 if(v.num == 0.) 696 return false; 697 TStr => 698 if(v.str == "") 699 return false; 700 TObj => 701 break; 702 TRegExp => 703 break; 704 * => 705 runtime(ex, TypeError, "unknown type in toBoolean"); 706 } 707 return true; 708} 709 710toNumber(ex: ref Exec, v: ref Val): real 711{ 712 case v.ty{ 713 TUndef => 714 return NaN; 715 TNull => 716 return 0.; 717 TBool => 718 if(v == false) 719 return 0.; 720 return 1.; 721 TNum => 722 return v.num; 723 TStr => 724 (si, r) := parsenum(ex, v.str, 0, ParseReal|ParseHex|ParseTrim|ParseEmpty); 725 if(si != len v.str) 726 r = Math->NaN; 727 return r; 728 TObj => 729 return toNumber(ex, toPrimitive(ex, v, TNum)); 730 TRegExp => 731 return NaN; 732 * => 733 runtime(ex, TypeError, "unknown type in toNumber"); 734 return 0.; 735 } 736} 737 738toInteger(ex: ref Exec, v: ref Val): real 739{ 740 r := toNumber(ex, v); 741 if(isnan(r)) 742 return 0.; 743 if(r == 0. || r == +Infinity || r == -Infinity) 744 return r; 745 return copysign(floor(fabs(r)), r); 746} 747 748# 749# toInt32 == toUint32, except for numbers > 2^31 750# 751toInt32(ex: ref Exec, v: ref Val): int 752{ 753 r := toNumber(ex, v); 754 if(isnan(r) || r == 0. || r == +Infinity || r == -Infinity) 755 return 0; 756 r = copysign(floor(fabs(r)), r); 757 # need to convert to big since it might be unsigned 758 return int big fmod(r, 4294967296.); 759} 760 761toUint32(ex: ref Exec, v: ref Val): big 762{ 763 r := toNumber(ex, v); 764 if(isnan(r) || r == 0. || r == +Infinity || r == -Infinity) 765 return big 0; 766 r = copysign(floor(fabs(r)), r); 767 # need to convert to big since it might be unsigned 768 b := big fmod(r, 4294967296.); 769 if(b < big 0) 770 fatal(ex, "uint32 < 0"); 771 return b; 772} 773 774toUint16(ex: ref Exec, v: ref Val): int 775{ 776 return toInt32(ex, v) & 16rffff; 777} 778 779toString(ex: ref Exec, v: ref Val): string 780{ 781 case v.ty{ 782 TUndef => 783 return "undefined"; 784 TNull => 785 return "null"; 786 TBool => 787 if(v == false) 788 return "false"; 789 return "true"; 790 TNum => 791 r := v.num; 792 if(isnan(r)) 793 return "NaN"; 794 if(r == 0.) 795 return "0"; 796 if(r == Infinity) 797 return "Infinity"; 798 if(r == -Infinity) 799 return "-Infinity"; 800 # this is wrong, but right is too hard 801 if(r < 1000000000000000000000. && r >= 1./(1000000.)){ 802 return string r; 803 } 804 return string r; 805 TStr => 806 return v.str; 807 TObj => 808 return toString(ex, toPrimitive(ex, v, TStr)); 809 TRegExp => 810 return "/" + v.rev.p + "/" + v.rev.f; 811 * => 812 runtime(ex, TypeError, "unknown type in ToString"); 813 return ""; 814 } 815} 816 817toObject(ex: ref Exec, v: ref Val): ref Obj 818{ 819 case v.ty{ 820 TUndef => 821 runtime(ex, TypeError, "can't convert undefined to an object"); 822 TNull => 823 runtime(ex, TypeError, "can't convert null to an object"); 824 TBool or 825 TStr or 826 TNum or 827 TRegExp => 828 return coerceToObj(ex, v).obj; 829 TObj => 830 return v.obj; 831 * => 832 runtime(ex, TypeError, "unknown type in toObject"); 833 return nil; 834 } 835 return nil; 836} 837