diff options
Diffstat (limited to 'appl/lib/ecmascript/builtin.b')
| -rw-r--r-- | appl/lib/ecmascript/builtin.b | 1480 |
1 files changed, 1480 insertions, 0 deletions
diff --git a/appl/lib/ecmascript/builtin.b b/appl/lib/ecmascript/builtin.b new file mode 100644 index 00000000..c8374f35 --- /dev/null +++ b/appl/lib/ecmascript/builtin.b @@ -0,0 +1,1480 @@ +# +# 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 <= 'a') + 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); +} |
