ref: 7a29422cfefaf76315eafa82ca606f9ded9517b3
dir: /appl/lib/ecmascript/builtin.b/
# # utility functions # biinst(o: ref Obj, bi: Builtin, p: ref Obj, h: ESHostobj): ref Obj { bo := mkobj(p, "Function"); bo.call = mkcall(nil, bi.params); bo.val = strval(bi.val); bo.host = h; varinstant(bo, DontEnum|DontDelete|ReadOnly, "length", ref RefVal(numval(real bi.length))); varinstant(o, DontEnum, bi.name, ref RefVal(objval(bo))); return bo; } biminst(o: ref Obj, bis: array of Builtin, p: ref Obj, h: ESHostobj) { for(i := 0; i < len bis; i++) biinst(o, bis[i], p, h); } biarg(args: array of ref Val, i: int): ref Val { if(i < len args) return args[i]; return undefined; } # # interface to builtin objects # get(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val { return esget(ex, o, property, 1); } put(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string, val: ref Ecmascript->Val) { return esput(ex, o, property, val, 1); } canput(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val { return escanput(ex, o, property, 1); } hasproperty(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val { return eshasproperty(ex, o, property, 1); } delete(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string) { return esdelete(ex, o, property, 1); } defaultval(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, tyhint: int): ref Ecmascript->Val { return esdefaultval(ex, o, tyhint, 1); } call(ex: ref Ecmascript->Exec, f, this: ref Ecmascript->Obj, args: array of ref Ecmascript->Val, eval: int): ref Ecmascript->Ref { x, y: real; v: ref Val; if(this == nil) this = ex.global; if(f.host != me) return escall(ex, f, this, args, eval); case f.val.str{ "eval" => v = ceval(ex, f, this, args); "parseInt" => v = cparseInt(ex, f, this, args); "parseFloat" => v = cparseFloat(ex, f, this, args); "escape" => v = cescape(ex, f, this, args); "unescape" => v = cunescape(ex, f, this, args); "isNaN" => v = cisNaN(ex, f, this, args); "isFinite" => v = cisFinite(ex, f, this, args); "decodeURI" => v = cdecodeuri(ex, f, this, args); "encodeURI" => v = cencodeuri(ex, f, this, args); "decodeURIComponent" => v = cdecodeuric(ex, f, this, args); "encodeURIComponent" => v = cencodeuric(ex, f, this, args); "Object" => v = cobj(ex, f, this, args); "Object.prototype.toString" or "Object.prototype.toLocaleString" => v = cobjprototoString(ex, f, this, args); "Object.prototype.valueOf" => v = cobjprotovalueOf(ex, f, this, args); "Object.prototype.hasOwnProperty" => v = cobjprotohasownprop(ex, f, this, args); "Object.prototype.isPrototypeOf" => v = cobjprotoisprotoof(ex, f, this, args); "Object.prototype.propertyisEnumerable" => v = cobjprotopropisenum(ex, f, this, args); "Function" => v = objval(nfunc(ex, f, args)); "Function.Prototype" => v = undefined; "Function.prototype.toString" => v = cfuncprototoString(ex, f, this, args); "Function.prototype.apply" => v = cfuncprotoapply(ex, f, this, args); "Function.prototype.call" => v = cfuncprotocall(ex, f, this, args); "Error" => v = objval(nerr(ex, f, args, ex.errproto)); "Error.prototype.toString" => v = cerrprototoString(ex, f, this, args); "EvalError" => v = objval(nerr(ex, f, args, ex.evlerrproto)); "EvalError.prototype.toString" => v = cerrprototoString(ex, f, this, args); "RangeError" => v = objval(nerr(ex, f, args, ex.ranerrproto)); "RangeError.prototype.toString" => v = cerrprototoString(ex, f, this, args); "ReferenceError" => v = objval(nerr(ex, f, args, ex.referrproto)); "ReferenceError.prototype.toString" => v = cerrprototoString(ex, f, this, args); "SyntaxError" => v = objval(nerr(ex, f, args, ex.synerrproto)); "SyntaxError.prototype.toString" => v = cerrprototoString(ex, f, this, args); "TypeError" => v = objval(nerr(ex, f, args, ex.typerrproto)); "TypeError.prototype.toString" => v = cerrprototoString(ex, f, this, args); "URIError" => v = objval(nerr(ex, f, args, ex.urierrproto)); "URIError.prototype.toString" => v = cerrprototoString(ex, f, this, args); "InternalError" => v = objval(nerr(ex, f, args, ex.interrproto)); "InternalError.prototype.toString" => v = cerrprototoString(ex, f, this, args); "Array" => v = objval(narray(ex, f, args)); "Array.prototype.toString" or "Array.prototype.toLocaleString" => v = carrayprototoString(ex, f, this, args); "Array.prototype.concat" => v = carrayprotoconcat(ex, f, this, args); "Array.prototype.join" => v = carrayprotojoin(ex, f, this, args); "Array.prototype.pop" => v = carrayprotopop(ex, f, this, args); "Array.prototype.push" => v = carrayprotopush(ex, f, this, args); "Array.prototype.reverse" => v = carrayprotoreverse(ex, f, this, args); "Array.prototype.shift" => v = carrayprotoshift(ex, f, this, args); "Array.prototype.slice" => v = carrayprotoslice(ex, f, this, args); "Array.prototype.splice" => v = carrayprotosplice(ex, f, this, args); "Array.prototype.sort" => v = carrayprotosort(ex, f, this, args); "Array.prototype.unshift" => v = carrayprotounshift(ex, f, this, args); "String" => v = cstr(ex, f, this, args); "String.fromCharCode" => v = cstrfromCharCode(ex, f, this, args); "String.prototype.toString" => v = cstrprototoString(ex, f, this, args); "String.prototype.valueOf" => v = cstrprototoString(ex, f, this, args); "String.prototype.charAt" => v = cstrprotocharAt(ex, f, this, args); "String.prototype.charCodeAt" => v = cstrprotocharCodeAt(ex, f, this, args); "String.prototype.concat" => v = cstrprotoconcat(ex, f, this, args); "String.prototype.indexOf" => v = cstrprotoindexOf(ex, f, this, args); "String.prototype.lastIndexOf" => v = cstrprotolastindexOf(ex, f, this, args); "String.prototype.localeCompare" => v = cstrprotocmp(ex, f, this, args); "String.prototype.slice" => v = cstrprotoslice(ex, f, this, args); "String.prototype.split" => v = cstrprotosplit(ex, f, this, args); "String.prototype.substr" => v = cstrprotosubstr(ex, f, this, args); "String.prototype.substring" => v = cstrprotosubstring(ex, f, this, args); "String.prototype.toLowerCase" or "String.prototype.toLocaleLowerCase" => v = cstrprototoLowerCase(ex, f, this, args); "String.prototype.toUpperCase" or "String.prototype.toLocaleUpperCase" => v = cstrprototoUpperCase(ex, f, this, args); "String.prototype.match" => v = cstrprotomatch(ex, f, this, args); "String.prototype.replace" => v = cstrprotoreplace(ex, f, this, args); "String.prototype.search" => v = cstrprotosearch(ex, f, this, args); # JavaScript 1.0 "String.prototype.anchor" or "String.prototype.big" or "String.prototype.blink" or "String.prototype.bold" or "String.prototype.fixed" or "String.prototype.fontcolor" or "String.prototype.fontsize" or "String.prototype.italics" or "String.prototype.link" or "String.prototype.small" or "String.prototype.strike" or "String.prototype.sub" or "String.prototype.sup" => s := toString(ex, objval(this)); arg := toString(ex, biarg(args, 0)); tag, endtag: string; case f.val.str{ "String.prototype.anchor" => tag = "<A NAME=\"" + arg + "\">"; endtag = "</A>"; "String.prototype.big" => tag = "<BIG>"; endtag = "</BIG>"; "String.prototype.blink" => tag = "<BLINK>"; endtag = "</BLINK>"; "String.prototype.bold" => tag = "<B>"; endtag = "</B>"; "String.prototype.fixed" => tag = "<TT>"; endtag = "</TT>"; "String.prototype.fontcolor" => tag = "<FONT COLOR=\"" + arg + "\">"; endtag = "</FONT>"; "String.prototype.fontsize" => tag = "<FONT SIZE=\"" + arg + "\">"; endtag = "</FONT>"; "String.prototype.italics" => tag = "<I>"; endtag = "</I>"; "String.prototype.link" => tag = "<A HREF=\"" + arg + "\">"; endtag = "</A>"; "String.prototype.small" => tag = "<SMALL>"; endtag = "</SMALL>"; "String.prototype.strike" => tag = "<STRIKE>"; endtag = "</STRIKE>"; "String.prototype.sub" => tag = "<SUB>"; endtag = "</SUB>"; "String.prototype.sup" => tag = "<SUP>"; endtag = "</SUP>"; } v = strval(tag + s + endtag); "Boolean" => v = cbool(ex, f, this, args); "Boolean.prototype.toString" => v = cboolprototoString(ex, f, this, args); "Boolean.prototype.valueOf" => v = cboolprotovalueOf(ex, f, this, args); "Number" => v = cnum(ex, f, this, args); "Number.prototype.toString" or "Number.prototype.toLocaleString" => v = cnumprototoString(ex, f, this, args); "Number.prototype.valueOf" => v = cnumprotovalueOf(ex, f, this, args); "Number.prototype.toFixed" => v = cnumprotofix(ex, f, this, args); "Number.prototype.toExponential" => v = cnumprotoexp(ex, f, this, args); "Number.prototype.toPrecision" => v = cnumprotoprec(ex, f, this, args); "RegExp" => v = cregexp(ex, f, this, args); "RegExp.prototype.exec" => v = cregexpprotoexec(ex, f, this, args); "RegExp.prototype.test" => v = cregexpprototest(ex, f, this, args); "RegExp.prototype.toString" => v = cregexpprototoString(ex, f, this, args); "Math.abs" or "Math.acos" or "Math.asin" or "Math.atan" or "Math.ceil" or "Math.cos" or "Math.exp" or "Math.floor" or "Math.log" or "Math.round" or "Math.sin" or "Math.sqrt" or "Math.tan" => x = toNumber(ex, biarg(args, 0)); case f.val.str{ "Math.abs" => if(x < 0.) x = -x; else if(x == 0.) x = 0.; "Math.acos" => x = math->acos(x); "Math.asin" => x = math->asin(x); "Math.atan" => x = math->atan(x); "Math.ceil" => x = math->ceil(x); "Math.cos" => x = math->cos(x); "Math.exp" => x = math->exp(x); "Math.floor" => x = math->floor(x); "Math.log" => x = math->log(x); "Math.round" => if((x == .0 && copysign(1., x) == -1.) || (x < .0 && x >= -0.5)) x = -0.; else x = math->floor(x+.5); "Math.sin" => x = math->sin(x); "Math.sqrt" => x = math->sqrt(x); "Math.tan" => x = math->tan(x); } v = numval(x); "Math.random" => # range := big 16r7fffffffffffffff; range := big 1000000000; v = numval(real bigrand(range)/ real range); "Math.atan2" or "Math.max" or "Math.min" or "Math.pow" => x = toNumber(ex, biarg(args, 0)); y = toNumber(ex, biarg(args, 1)); case f.val.str{ "Math.atan2" => x = math->atan2(x, y); "Math.max" => if(x > y) ; else if(x < y) x = y; else if(x == y){ if(x == 0. && copysign(1., x) == -1. && copysign(1., y) == 1.) x = y; }else x = Math->NaN; "Math.min" => if(x < y) ; else if(x > y) x = y; else if(x == y){ if(x == 0. && copysign(1., x) == 1. && copysign(1., y) == -1.) x = y; }else x = Math->NaN; "Math.pow" => x = math->pow(x, y); } v = numval(x); "Date" => v = cdate(ex, f, this, args); "Date.parse" => v = cdateparse(ex, f, this, args); "Date.UTC" => v = cdateUTC(ex, f, this, args); "Date.prototype.toString" or "Date.prototype.toLocaleString" => v = cdateprototoString(ex, f, this, args); "Date.prototype.toDateString" or "Date.prototype.toLocaleDateString" => v = cdateprototoDateString(ex, f, this, args); "Date.prototype.toTimeString" or "Date.prototype.toLocaleTimeString" => v = cdateprototoTimeString(ex, f, this, args); "Date.prototype.valueOf" or "Date.prototype.getTime" => v = cdateprotovalueOf(ex, f, this, args); "Date.prototype.getYear" or "Date.prototype.getFullYear" or "Date.prototype.getMonth" or "Date.prototype.getDate" or "Date.prototype.getDay" or "Date.prototype.getHours" or "Date.prototype.getMinutes" or "Date.prototype.getSeconds" => v = cdateprotoget(ex, f, this, args, !UTC); "Date.prototype.getUTCFullYear" or "Date.prototype.getUTCMonth" or "Date.prototype.getUTCDate" or "Date.prototype.getUTCDay" or "Date.prototype.getUTCHours" or "Date.prototype.getUTCMinutes" or "Date.prototype.getUTCSeconds" => v = cdateprotoget(ex, f, this, args, UTC); "Date.prototype.getMilliseconds" or "Date.prototype.getUTCMilliseconds" => v = cdateprotogetMilliseconds(ex, f, this, args); "Date.prototype.getTimezoneOffset" => v = cdateprotogetTimezoneOffset(ex, f, this, args); "Date.prototype.setTime" => v = cdateprotosetTime(ex, f, this, args); "Date.prototype.setMilliseconds" => v = cdateprotosetMilliseconds(ex, f, this, args, !UTC); "Date.prototype.setUTCMilliseconds" => v = cdateprotosetMilliseconds(ex, f, this, args, UTC); "Date.prototype.setSeconds" => v = cdateprotosetSeconds(ex, f, this, args, !UTC); "Date.prototype.setUTCSeconds" => v = cdateprotosetSeconds(ex, f, this, args, UTC); "Date.prototype.setMinutes" => v = cdateprotosetMinutes(ex, f, this, args, !UTC); "Date.prototype.setUTCMinutes" => v = cdateprotosetMinutes(ex, f, this, args, UTC); "Date.prototype.setHours" => v = cdateprotosetHours(ex, f, this, args, !UTC); "Date.prototype.setUTCHours" => v = cdateprotosetHours(ex, f, this, args, UTC); "Date.prototype.setDate" => v = cdateprotosetDate(ex, f, this, args, !UTC); "Date.prototype.setUTCDate" => v = cdateprotosetDate(ex, f, this, args, UTC); "Date.prototype.setMonth" => v = cdateprotosetMonth(ex, f, this, args, !UTC); "Date.prototype.setUTCMonth" => v = cdateprotosetMonth(ex, f, this, args, UTC); "Date.prototype.setFullYear" => v = cdateprotosetFullYear(ex, f, this, args, !UTC); "Date.prototype.setUTCFullYear" => v = cdateprotosetFullYear(ex, f, this, args, UTC); "Date.prototype.setYear" => v = cdateprotosetYear(ex, f, this, args); "Date.prototype.toUTCString" or "Date.prototype.toGMTString" => v = cdateprototoUTCString(ex, f, this, args); * => v = nil; } if(v == nil) runtime(ex, ReferenceError, "unknown function "+f.val.str+" in builtin call"); return valref(v); } rsalt := big 12345678; randinit(seed: big) { rsalt = big seed; bigrand(big 1); bigrand(big 1); } RANDMASK: con (big 1<<63)-(big 1); bigrand(modulus: big): big { rsalt = rsalt * big 1103515245 + big 12345; if(modulus <= big 0) return big 0; return ((rsalt&RANDMASK)>>10) % modulus; } construct(ex: ref Ecmascript->Exec, f: ref Ecmascript->Obj, args: array of ref Ecmascript->Val): ref Ecmascript->Obj { if(f.host != me) runtime(ex, TypeError, "ecmascript builtin called incorrectly"); case f.val.str{ "Object" => return nobj(ex, f, args); "Function" => return nfunc(ex, f, args); "Array" => return narray(ex, f, args); "Error" => return nerr(ex, f, args, ex.errproto); "EvalError" => return nerr(ex, f, args, ex.evlerrproto); "RangeError" => return nerr(ex, f, args, ex.ranerrproto); "ReferenceError" => return nerr(ex, f, args, ex.referrproto); "SyntaxError" => return nerr(ex, f, args, ex.synerrproto); "TypeError" => return nerr(ex, f, args, ex.typerrproto); "URIError" => return nerr(ex, f, args, ex.urierrproto); "InternalError" => return nerr(ex, f, args, ex.interrproto); "String" or "Boolean" or "Number" => return coerceToObj(ex, call(ex, f, nil, args, 0).val).obj; "Date" => return ndate(ex, f, args); "RegExp" => return nregexp(ex, f, args); } runtime(ex, ReferenceError, "unknown constructor "+f.val.str+" in builtin construct"); return nil; } ceval(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { if(len args < 1) return undefined; vs := coerceToVal(args[0]); if(!isstr(vs)) return args[0]; (k, v, nil) := eval(ex, vs.str); if(k != CNormal || v == nil) v = undefined; return v; } cparseInt(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { sv := biarg(args, 0); s := toString(ex, sv); neg := 0; i := 0; if(len s > i){ if(s[i] == '-'){ neg = 1; i++; }else if(s[i] == '+') i++; } rv := biarg(args, 1); if(rv == undefined) r := big 0; else r = big toInt32(ex, rv); if(r == big 0){ if(len s > i && s[i] == '0'){ r = big 8; if(len s >= i+2 && (s[i+1] == 'x' || s[i+1] == 'X')) r = big 16; }else r = big 10; }else if(r < big 0 || r > big 36) return numval(Math->NaN); if(r == big 16 && len s >= i+2 && s[i] == '0' && (s[i+1] == 'x' || s[i+1] == 'X')) i += 2; ok := 0; n := big 0; for(; i < len s; i++) { c := s[i]; v := r; case c { 'a' to 'z' => v = big(c - 'a' + 10); 'A' to 'Z' => v = big(c - 'A' + 10); '0' to '9' => v = big(c - '0'); } if(v >= r) break; ok = 1; n = n * r + v; } if(!ok) return numval(Math->NaN); if(neg) n = -n; return numval(real n); } cparseFloat(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, biarg(args, 0)); (nil, r) := parsenum(ex, s, 0, ParseReal|ParseTrim); return numval(r); } cescape(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, biarg(args, 0)); t := ""; for(i := 0; i < len s; i++){ c := s[i]; case c{ 'A' to 'Z' or 'a' to 'z' or '0' to '9' or '@' or '*' or '_' or '+' or '-' or '.' or '/' => t[len t] = s[i]; * => e := ""; do{ d := c & 16rf; e = "0123456789abcdef"[d:d+1] + e; c >>= 4; }while(c); if(len e & 1) e = "0" + e; if(len e == 4) e = "u" + e; t += "%" + e; } } return strval(t); } cunescape(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, biarg(args, 0)); t := ""; for(i := 0; i < len s; i++){ c := s[i]; if(c == '%'){ if(i + 5 < len s && s[i+1] == 'u'){ (v, e) := str->toint(s[i+2:i+6], 16); if(e == ""){ c = v; i += 5; } }else if(i + 2 < len s){ (v, e) := str->toint(s[i+1:i+3], 16); if(e == ""){ c = v; i += 2; } } } t[len t] = c; } return strval(t); } cisNaN(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { if(math->isnan(toNumber(ex, biarg(args, 0)))) return true; return false; } cisFinite(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { r := toNumber(ex, biarg(args, 0)); if(math->isnan(r) || r == +Infinity || r == -Infinity) return false; return true; } cobj(ex: ref Exec, f, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { o: ref Obj; v := biarg(args, 0); if(isnull(v) || isundefined(v)) o = nobj(ex, f, args); else o = toObject(ex, v); return objval(o); } nobj(ex: ref Exec, nil: ref Ecmascript->Obj, args: array of ref Val): ref Ecmascript->Obj { o: ref Obj; v := biarg(args, 0); case v.ty{ TNull or TUndef => o = mkobj(ex.objproto, "Object"); TBool => o = mkobj(ex.boolproto, "Boolean"); o.val = v; TStr => o = mkobj(ex.strproto, "String"); o.val = v; varinstant(o, DontEnum|DontDelete|ReadOnly, "length", ref RefVal(numval(real len v.str))); TNum => o = mkobj(ex.numproto, "Number"); o.val = v; TObj => o = v.obj; TRegExp => o = mkobj(ex.regexpproto, "RegExp"); o.val = v; varinstant(o, DontEnum|DontDelete|ReadOnly, "length", ref RefVal(numval(real len v.rev.p))); varinstant(o, DontEnum|DontDelete|ReadOnly, "source", ref RefVal(strval(v.rev.p))); varinstant(o, DontEnum|DontDelete|ReadOnly, "global", ref RefVal(strhas(v.rev.f, 'g'))); varinstant(o, DontEnum|DontDelete|ReadOnly, "ignoreCase", ref RefVal(strhas(v.rev.f, 'i'))); varinstant(o, DontEnum|DontDelete|ReadOnly, "multiline", ref RefVal(strhas(v.rev.f, 'm'))); varinstant(o, DontEnum|DontDelete, "lastIndex", ref RefVal(numval(real v.rev.i))); * => runtime(ex, ReferenceError, "unknown type in Object constructor"); } return o; } cobjprototoString(nil: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { return strval("[object " + this.class + "]"); } cobjprotovalueOf(nil: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { return objval(this); } cobjprotohasownprop(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { o := this; s := toString(ex, biarg(args, 0)); p := o.prototype; o.prototype = nil; v := eshasproperty(ex, o, s, 0); o.prototype = p; return v; } cobjprotoisprotoof(nil: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { o := this; v := biarg(args, 0); if(!isobj(v)) return false; for(p := v.obj.prototype; p != nil; p = p.prototype) if(p == o) return true; return false; } cobjprotopropisenum(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { return eshasenumprop(this, toString(ex, biarg(args, 0))); } nfunc(ex: ref Exec, nil: ref Ecmascript->Obj, args: array of ref Val): ref Ecmascript->Obj { params := ""; body := ""; sep := ""; for(i := 0; i < len args - 1; i++){ params += sep + toString(ex, args[i]); sep = ","; } if(i < len args) body = toString(ex, args[i]); p := mkparser(ex, "function anonymous("+params+"){"+body+"}"); fundecl(ex, p, 0); if(p.errors) runtime(ex, SyntaxError, ex.error); if(p.code.vars[0].name != "anonymous") runtime(ex, SyntaxError, "parse failure"); return p.code.vars[0].val.val.obj; } cfuncprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { if(this.call == nil) runtime(ex, TypeError, "Function.prototype.toString called for a non-Function object"); return strval(funcprint(ex, this)); } nerr(ex: ref Exec, f: ref Ecmascript->Obj, args: array of ref Val, proto: ref Obj): ref Ecmascript->Obj { msg := biarg(args, 0); if(msg == undefined) s := ""; else s = toString(ex, msg); o := mkobj(proto, f.val.str); varinstant(o, DontEnum|DontDelete|ReadOnly, "length", ref RefVal(numval(real 1))); varinstant(o, DontEnum|DontDelete, "name", ref RefVal(strval(f.val.str))); varinstant(o, DontEnum|DontDelete, "message", ref RefVal(strval(s))); return o; } cfuncprotoapply(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { ar: ref Obj; if(this.call == nil || !isfuncobj(this)) runtime(ex, TypeError, "Function.prototype.apply called for a non-Function object"); v := biarg(args, 0); if(v == null || v == undefined) th := ex.global; else th = coerceToObj(ex, v).obj; v = biarg(args, 1); if(v == null || v == undefined) l := 0; else{ if(!isobj(v)) runtime(ex, TypeError, "Function.prototype.apply non-array argument"); ar = v.obj; v = esget(ex, ar, "length", 0); if(v == undefined) runtime(ex, TypeError, "Function.prototype.apply non-array argument"); l = int toUint32(ex, v); } args = array[l] of ref Val; for(i := 0; i < l; i++) args[i] = esget(ex, ar, string i, 0); return getValue(ex, escall(ex, this, th, args, 0)); } cfuncprotocall(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { if(this.call == nil || !isfuncobj(this)) runtime(ex, TypeError, "Function.prototype.call called for a non-Function object"); v := biarg(args, 0); if(v == null || v == undefined) th := ex.global; else th = coerceToObj(ex, v).obj; return getValue(ex, escall(ex, this, th, args[1: ], 0)); } cerrprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { return esget(ex, this, "message", 0); } narray(ex: ref Exec, nil: ref Ecmascript->Obj, args: array of ref Val): ref Ecmascript->Obj { o := mkobj(ex.arrayproto, "Array"); length := big len args; if(length == big 1 && isnum(coerceToVal(args[0]))){ length = toUint32(ex, args[0]); varinstant(o, DontEnum|DontDelete, "length", ref RefVal(numval(real length))); }else{ varinstant(o, DontEnum|DontDelete, "length", ref RefVal(numval(real length))); for(i := 0; i < len args; i++) esput(ex, o, string i, args[i], 0); } return o; } carrayprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { return carrayprotojoin(ex, nil, this, nil); } carrayprotoconcat(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { v: ref Val; e: ref Obj; a := narray(ex, nil, nil); n := 0; nargs := len args; for(i := -1; i < nargs; i++){ if(i < 0){ e = this; v = objval(e); } else{ v = biarg(args, i); if(isobj(v)) e = v.obj; else e = nil; } if(e != nil && isarray(e)){ leng := int toUint32(ex, esget(ex, e, "length", 0)); for(k := 0; k < leng; k++){ av := esget(ex, e, string k, 0); if(v != undefined) esput(ex, a, string n, av, 0); n++; } } else{ esput(ex, a, string n, v, 0); n++; } } esput(ex, a, "length", numval(real n), 0); return objval(a); } carrayprotojoin(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { length := toUint32(ex, esget(ex, this, "length", 0)); sepv := biarg(args, 0); sep := ","; if(sepv != undefined) sep = toString(ex, sepv); s := ""; ss := ""; for(i := big 0; i < length; i++){ tv := esget(ex, this, string i, 0); t := ""; if(tv != undefined && !isnull(tv)) t = toString(ex, tv); s += ss + t; ss = sep; } return strval(s); } carrayprotoreverse(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { length := toUint32(ex, esget(ex, this, "length", 0)); mid := length / big 2; for(i := big 0; i < mid; i++){ i1 := string i; v1 := esget(ex, this, i1, 0); i2 := string(length - i - big 1); v2 := esget(ex, this, i2, 0); if(v2 == undefined) esdelete(ex, this, i1, 0); else esput(ex, this, i1, v2, 0); if(v1 == undefined) esdelete(ex, this, i2, 0); else esput(ex, this, i2, v1, 0); } return objval(this); } carrayprotopop(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { leng := toUint32(ex, esget(ex, this, "length", 0)); if(leng == big 0){ esput(ex, this, "length", numval(0.), 0); return undefined; } ind := string (leng-big 1); v := esget(ex, this, ind, 0); esdelete(ex, this, ind, 0); esput(ex, this, "length", numval(real (leng-big 1)), 0); return v; } carrayprotopush(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { leng := toUint32(ex, esget(ex, this, "length", 0)); nargs := len args; for(i := 0; i < nargs; i++) esput(ex, this, string (leng+big i), biarg(args, i), 0); nv := numval(real (leng+big nargs)); esput(ex, this, "length", nv, 0); return nv; } carrayprotoshift(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { leng := int toUint32(ex, esget(ex, this, "length", 0)); if(leng == 0){ esput(ex, this, "length", numval(0.), 0); return undefined; } v0 := esget(ex, this, "0", 0); for(k := 1; k < leng; k++){ v := esget(ex, this, string k, 0); if(v == undefined) esdelete(ex, this, string (k-1), 0); else esput(ex, this, string (k-1), v, 0); } esdelete(ex, this, string (leng-1), 0); esput(ex, this, "length", numval(real (leng-1)), 0); return v0; } carrayprotounshift(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { leng := int toUint32(ex, esget(ex, this, "length", 0)); nargs := len args; for(i := leng-1; i >= 0; i--){ v := esget(ex, this, string i, 0); if(v == undefined) esdelete(ex, this, string (i+nargs), 0); else esput(ex, this, string (i+nargs), v, 0); } for(i = 0; i < nargs; i++) esput(ex, this, string i, biarg(args, i), 0); nv := numval(real (leng+nargs)); esput(ex, this, "length", nv, 0); return nv; } carrayprotoslice(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { a := narray(ex, nil, nil); leng := int toUint32(ex, esget(ex, this, "length", 0)); start := toInt32(ex, biarg(args, 0)); if(start < 0) start += leng; if(start < 0) start = 0; if(start > leng) start = leng; if(biarg(args, 1) == undefined) end := leng; else end = toInt32(ex, biarg(args, 1)); if(end < 0) end += leng; if(end < 0) end = 0; if(end > leng) end = leng; n := 0; for(k := start; k < end; k++){ v := esget(ex, this, string k, 0); if(v != undefined) esput(ex, a, string n, v, 0); n++; } esput(ex, a, "length", numval(real n), 0); return objval(a); } carrayprotosplice(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { a := narray(ex, nil, nil); leng := int toUint32(ex, esget(ex, this, "length", 0)); start := toInt32(ex, biarg(args, 0)); if(start < 0) start += leng; if(start < 0) start = 0; if(start > leng) start = leng; delc := toInt32(ex, biarg(args, 1)); if(delc < 0) delc = 0; if(start+delc > leng) delc = leng-start; for(k := 0; k < delc; k++){ v := esget(ex, this, string (k+start), 0); if(v != undefined) esput(ex, a, string k, v, 0); } esput(ex, a, "length", numval(real delc), 0); nargs := len args - 2; if(nargs < delc){ for(k = start; k < leng-delc; k++){ v := esget(ex, this, string (k+delc), 0); if(v == undefined) esdelete(ex, this, string (k+nargs), 0); else esput(ex, this, string (k+nargs), v, 0); } for(k = leng; k > leng-delc+nargs; k--) esdelete(ex, this, string (k-1), 0); } else if(nargs > delc){ for(k = leng-delc; k > start; k--){ v := esget(ex, this, string (k+delc-1), 0); if(v == undefined) esdelete(ex, this, string (k+nargs-1), 0); else esput(ex, this, string (k+nargs-1), v, 0); } } for(k = start; k < start+nargs; k++) esput(ex, this, string k, biarg(args, k-start+2), 0); esput(ex, this, "length", numval(real (leng-delc+nargs)), 0); return objval(a); } carrayprotosort(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { length := toUint32(ex, esget(ex, this, "length", 0)); cmp := biarg(args, 0); if(cmp == undefined) cmp = nil; else if(!isobj(cmp) || cmp.obj.call == nil) runtime(ex, TypeError, "Array.prototype.sort argument is not a function"); # # shell sort # for(m := (length+big 3)/big 5; m > big 0; m = (m+big 1)/big 3){ for(i := length-m; i-- != big 0;){ v1, v2 : ref Val = nil; ji := big -1; for(j := i+m; j < length; j += m){ if(v1 == nil) v1 = esget(ex, this, string(j-m), 0); v2 = esget(ex, this, string(j), 0); cr : real; if(v1 == undefined && v2 == undefined) cr = 0.; else if(v1 == undefined) cr = 1.; else if(v2 == undefined) cr = -1.; else if(cmp == nil){ s1 := toString(ex, v1); s2 := toString(ex, v2); if(s1 < s2) cr = -1.; else if(s1 > s2) cr = 1.; else cr = 0.; }else{ # # this value not specified by docs # cr = toNumber(ex, getValue(ex, escall(ex, cmp.obj, this, array[] of {v1, v2}, 0))); } if(cr <= 0.) break; if(v2 == undefined) esdelete(ex, this, string(j-m), 0); else esput(ex, this, string(j-m), v2, 0); ji = j; } if(ji != big -1){ if(v1 == undefined) esdelete(ex, this, string(ji), 0); else esput(ex, this, string(ji), v1, 0); } } } return objval(this); } cstr(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := ""; if(len args > 0) s = toString(ex, biarg(args, 0)); return strval(s); } cstrfromCharCode(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := ""; for(i := 0; i < len args; i++) s[i] = toUint16(ex, args[i]); return strval(s); } cstrprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { if(!isstrobj(this)) runtime(ex, TypeError, "String.prototype.toString called on non-String object"); return this.val; } cstrprotocharAt(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, objval(this)); rpos := toInteger(ex, biarg(args, 0)); if(rpos < 0. || rpos >= real len s) s = ""; else{ pos := int rpos; s = s[pos: pos+1]; } return strval(s); } cstrprotocharCodeAt(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, objval(this)); rpos := toInteger(ex, biarg(args, 0)); if(rpos < 0. || rpos >= real len s) c := Math->NaN; else c = real s[int rpos]; return numval(c); } cstrprotoindexOf(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, objval(this)); t := toString(ex, biarg(args, 0)); rpos := toInteger(ex, biarg(args, 1)); if(rpos < 0.) rpos = 0.; else if(rpos > real len s) rpos = real len s; lent := len t; stop := len s - lent; for(i := int rpos; i <= stop; i++) if(s[i:i+lent] == t) break; if(i > stop) i = -1; return numval(real i); } cstrprotolastindexOf(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, objval(this)); t := toString(ex, biarg(args, 0)); v := biarg(args, 1); rpos := toNumber(ex, v); if(math->isnan(rpos)) rpos = Math->Infinity; else rpos = toInteger(ex, v); if(rpos < 0.) rpos = 0.; else if(rpos > real len s) rpos = real len s; lent := len t; i := len s - lent; if(i > int rpos) i = int rpos; for(; i >= 0; i--) if(s[i:i+lent] == t) break; return numval(real i); } cstrprotosplit(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, objval(this)); a := narray(ex, nil, nil); tv := biarg(args, 0); ai := 0; if(tv == undefined) esput(ex, a, string ai, strval(s), 0); else{ t := toString(ex, tv); lent := len t; stop := len s - lent; pos := 0; if(lent == 0){ for(; pos < stop; pos++) esput(ex, a, string ai++, strval(s[pos:pos+1]), 0); }else{ for(k := pos; k <= stop; k++){ if(s[k:k+lent] == t){ esput(ex, a, string ai++, strval(s[pos:k]), 0); pos = k + lent; k = pos - 1; } } esput(ex, a, string ai, strval(s[pos:k]), 0); } } return objval(a); } cstrprotosubstring(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, objval(this)); rstart := toInteger(ex, biarg(args, 0)); lens := real len s; rend := lens; if(len args >= 2) rend = toInteger(ex, biarg(args, 1)); if(rstart < 0.) rstart = 0.; else if(rstart > lens) rstart = lens; if(rend < 0.) rend = 0.; else if(rend > lens) rend = lens; if(rstart > rend){ lens = rstart; rstart = rend; rend = lens; } return strval(s[int rstart: int rend]); } cstrprotosubstr(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, objval(this)); ls := len s; start := toInt32(ex, biarg(args, 0)); if(biarg(args, 1) == undefined) leng := ls; else leng = toInt32(ex, biarg(args, 1)); if(start < 0) start += ls; if(start < 0) start = 0; if(leng < 0) leng = 0; if(start+leng > ls) leng = ls-start; if(leng <= 0) s = ""; else s = s[start: start+leng]; return strval(s); } cstrprotoslice(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, objval(this)); ls := len s; start := toInt32(ex, biarg(args, 0)); if(biarg(args, 1) == undefined) end := ls; else end = toInt32(ex, biarg(args, 1)); if(start < 0) start += ls; if(start < 0) start = 0; if(start > ls) start = ls; if(end < 0) end += ls; if(end < 0) end = 0; if(end > ls) end = ls; leng := end-start; if(leng < 0) leng = 0; return strval(s[start: start+leng]); } cstrprotocmp(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, objval(this)); t := toString(ex, biarg(args, 0)); r := 0; if(s < t) r = -1; else if(s > t) r = 1; return numval(real r); } cstrprotoconcat(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, objval(this)); n := len args; for(i := 0; i < n; i++) s += toString(ex, biarg(args, i)); return strval(s); } # this doesn't use unicode tolower cstrprototoLowerCase(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { s := toString(ex, objval(this)); for(i := 0; i < len s; i++) s[i] = tolower(s[i]); return strval(s); } #this doesn't use unicode toupper cstrprototoUpperCase(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { s := toString(ex, objval(this)); for(i := 0; i < len s; i++) s[i] = toupper(s[i]); return strval(s); } cbool(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { return toBoolean(ex, biarg(args, 0)); } tolower(c: int): int { if(c >= 'A' && c <= 'Z') return c - 'A' + 'a'; return c; } toupper(c: int): int { if(c >= 'a' && c <= 'z') return c - 'a' + 'A'; return c; } cboolprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { if(!isboolobj(this)) runtime(ex, TypeError, "Boolean.prototype.toString called on non-Boolean object"); return strval(toString(ex, this.val)); } cboolprotovalueOf(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { if(!isboolobj(this)) runtime(ex, TypeError, "Boolean.prototype.valueOf called on non-Boolean object"); return this.val; } cnum(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { r := 0.; if(len args > 0) r = toNumber(ex, biarg(args, 0)); return numval(r); } cnumprototoString(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { if(!isnumobj(this)) runtime(ex, TypeError, "Number.prototype.toString called on non-Number object"); return this.val; } cnumprotovalueOf(ex: ref Exec, nil, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { if(!isnumobj(this)) runtime(ex, TypeError, "Number.prototype.valueOf called on non-Number object"); return strval(toString(ex, this.val)); } cnumprotofix(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { if(!isnumobj(this)) runtime(ex, TypeError, "Number.prototype.toFixed called on non-Number object"); v := biarg(args, 0); if(v == undefined) f := 0; else f = toInt32(ex, v); if(f < 0 || f > 20) runtime(ex, RangeError, "fraction digits out of range"); x := toNumber(ex, this.val); if(isnan(x) || x == Infinity || x == -Infinity) s := toString(ex, this.val); else s = sys->sprint("%.*f", f, x); return strval(s); } cnumprotoexp(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { if(!isnumobj(this)) runtime(ex, TypeError, "Number.prototype.toExponential called on non-Number object"); v := biarg(args, 0); if(v == undefined) f := 6; else f = toInt32(ex, v); if(f < 0 || f > 20) runtime(ex, RangeError, "fraction digits out of range"); x := toNumber(ex, this.val); if(isnan(x) || x == Infinity || x == -Infinity) s := toString(ex, this.val); else s = sys->sprint("%.*e", f, x); return strval(s); } cnumprotoprec(ex: ref Exec, nil, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { if(!isnumobj(this)) runtime(ex, TypeError, "Number.prototype.toPrecision called on non-Number object"); v := biarg(args, 0); if(v == undefined) return strval(toString(ex, this.val)); p := toInt32(ex, v); if(p < 1 || p > 21) runtime(ex, RangeError, "fraction digits out of range"); x := toNumber(ex, this.val); if(isnan(x) || x == Infinity || x == -Infinity) s := toString(ex, this.val); else{ y := x; if(y < 0.0) y = -y; er := math->log10(y); (e, ef) := math->modf(er); if(ef < 0.0) e--; if(e < -6 || e >=p) s = sys->sprint("%.*e", p-1, x); else s = sys->sprint("%.*f", p-1, x); } return strval(s); }