#
# 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 = "";
endtag = "";
"String.prototype.big" =>
tag = "";
endtag = "";
"String.prototype.blink" =>
tag = "";
"String.prototype.bold" =>
tag = "";
endtag = "";
"String.prototype.fixed" =>
tag = "";
endtag = "";
"String.prototype.fontcolor" =>
tag = "";
endtag = "";
"String.prototype.fontsize" =>
tag = "";
endtag = "";
"String.prototype.italics" =>
tag = "";
endtag = "";
"String.prototype.link" =>
tag = "";
endtag = "";
"String.prototype.small" =>
tag = "";
endtag = "";
"String.prototype.strike" =>
tag = "";
endtag = "";
"String.prototype.sub" =>
tag = "";
endtag = "";
"String.prototype.sup" =>
tag = "";
endtag = "";
}
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);
}