## want to use the value in a context which# prefers an object, so coerce schizo vals# to object versions#coerceToObj(ex: ref Exec, v: ref Val): ref Val{ o: ref Obj; case v.ty{ TBool => o = mkobj(ex.boolproto, "Boolean"); o.val = v; TStr => o = mkobj(ex.strproto, "String"); o.val = v; valinstant(o, DontEnum|DontDelete|ReadOnly, "length", numval(real len v.str)); TNum => o = mkobj(ex.numproto, "Number"); o.val = v; TRegExp => o = mkobj(ex.regexpproto, "RegExp"); o.val = v; valinstant(o, DontEnum|DontDelete|ReadOnly, "length", numval(real len v.rev.p)); valinstant(o, DontEnum|DontDelete|ReadOnly, "source", strval(v.rev.p)); valinstant(o, DontEnum|DontDelete|ReadOnly, "global", strhas(v.rev.f, 'g')); valinstant(o, DontEnum|DontDelete|ReadOnly, "ignoreCase", strhas(v.rev.f, 'i')); valinstant(o, DontEnum|DontDelete|ReadOnly, "multiline", strhas(v.rev.f, 'm')); valinstant(o, DontEnum|DontDelete, "lastIndex", numval(real v.rev.i)); * => return v; } return objval(o);} coerceToVal(v: ref Val): ref Val{ if(v.ty != TObj) return v; o := v.obj; if(o.host != nil && o.host != me || o.class != "String" || o.class != "Number" || o.class != "Boolean") return v; return o.val;} isstrobj(o: ref Obj): int{ return (o.host == nil || o.host == me) && o.class == "String";} isnumobj(o: ref Obj): int{ return (o.host == nil || o.host == me) && o.class == "Number";} isboolobj(o: ref Obj): int{ return (o.host == nil || o.host == me) && o.class == "Boolean";} isdateobj(o: ref Obj): int{ return (o.host == nil || o.host == me) && o.class == "Date";} isregexpobj(o: ref Obj): int{ return (o.host == nil || o.host == me) && o.class == "RegExp";} isfuncobj(o: ref Obj): int{ return (o.host == nil || o.host == me) && o.class == "Function";} isarray(o: ref Obj): int{# return (o.host == nil || o.host == me) && o.class == "Array"; # relax the host test # so that hosts can intercept Array operations and defer # unhandled ops to the builtin return o.class == "Array";} iserr(o: ref Obj): int{ return (o.host == nil || o.host == me) && o.class == "Error";} isactobj(o: ref Obj): int{ return o.host == nil && o.class == "Activation";} isnull(v: ref Val): int{ return v == null || v == nil;} isundefined(v: ref Val): int{ return v == undefined;} isstr(v: ref Val): int{ return v.ty == TStr;} isnum(v: ref Val): int{ return v.ty == TNum;} isbool(v: ref Val): int{ return v.ty == TBool;} isobj(v: ref Val): int{ return v.ty == TObj;} isregexp(v: ref Val): int{ return v.ty == TRegExp || v.ty == TObj && isregexpobj(v.obj);} ## retrieve the object field if it's valid#getobj(v: ref Val): ref Obj{ if(v.ty == TObj) return v.obj; return nil;} isprimval(v: ref Val): int{ return v.ty != TObj;} pushscope(ex: ref Exec, o: ref Obj){ ex.scopechain = o :: ex.scopechain;} popscope(ex: ref Exec){ ex.scopechain = tl ex.scopechain;} runtime(ex: ref Exec, o: ref Obj, s: string){ ex.error = s; if(o == nil) ex.errval = undefined; else ex.errval = objval(o); if(debug['r']){ print("ecmascript runtime error: %s\n", s); if(""[5] == -1); # abort } raise "throw"; exit; # never reached} mkobj(proto: ref Obj, class: string): ref Obj{ if(class == nil) class = "Object"; return ref Obj(nil, proto, nil, nil, nil, class, nil, nil);} valcheck(ex: ref Exec, v: ref Val, hint: int){ if(v == nil || v.ty < 0 || v.ty >= NoHint || v.ty == TBool && v != true && v != false || v.ty == TObj && v.obj == nil || hint != NoHint && v.ty != hint) runtime(ex, RangeError, "bad value generated by host object");} # builtin methods for propertiesesget(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val{ for( ; o != nil; o = o.prototype){ if(!force && o.host != nil && o.host != me){ v := o.host->get(ex, o, prop); valcheck(ex, v, NoHint); return v; } for(i := 0; i < len o.props; i++) if(o.props[i] != nil && o.props[i].name == prop) return o.props[i].val.val; force = 0; } return undefined;} esputind(o: ref Obj, prop: string): int{ empty := -1; props := o.props; for(i := 0; i < len props; i++){ if(props[i] == nil) empty = i; else if(props[i].name == prop) return i; } if(empty != -1) return empty; props = array[i+1] of ref Prop; props[:] = o.props; o.props = props; return i;} esput(ex: ref Exec, o: ref Obj, prop: string, v: ref Val, force: int){ ai: big; if(!force && o.host != nil && o.host != me) return o.host->put(ex, o, prop, v); if(escanput(ex, o, prop, 0) != true) return; # # should this test for prototype == ex.arrayproto? # hard to say, but 15.4.5 "Properties of Array Instances" implies not # if(isarray(o)) al := toUint32(ex, esget(ex, o, "length", 1)); i := esputind(o, prop); props := o.props; if(props[i] != nil) props[i].val.val = v; else props[i] = ref Prop(0, prop, ref RefVal(v)); if(!isarray(o)) return; if(prop == "length"){ nl := toUint32(ex, v); for(ai = nl; ai < al; ai++) esdelete(ex, o, string ai, 1); props[i].val.val = numval(real nl); }else{ ai = big prop; if(prop != string ai || ai < big 0 || ai >= 16rffffffff) return; i = esputind(o, "length"); if(props[i] == nil) fatal(ex, "bogus array esput"); else if(toUint32(ex, props[i].val.val) <= ai) props[i].val.val = numval(real(ai+big 1)); }} escanput(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val{ for( ; o != nil; o = o.prototype){ if(!force && o.host != nil && o.host != me){ v := o.host->canput(ex, o, prop); valcheck(ex, v, TBool); return v; } for(i := 0; i < len o.props; i++){ if(o.props[i] != nil && o.props[i].name == prop){ if(o.props[i].attr & ReadOnly) return false; else return true; } } force = 0; } return true;} eshasproperty(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val{ for(; o != nil; o = o.prototype){ if(!force && o.host != nil && o.host != me){ v := o.host->hasproperty(ex, o, prop); valcheck(ex, v, TBool); return v; } for(i := 0; i < len o.props; i++) if(o.props[i] != nil && o.props[i].name == prop) return true; } return false;} eshasenumprop(o: ref Obj, prop: string): ref Val{ for(i := 0; i < len o.props; i++) if(o.props[i] != nil && o.props[i].name == prop){ if(o.props[i].attr & DontEnum) return false; return true; } return false;} propshadowed(start, end: ref Obj, prop: string): int{ for(o := start; o != end; o = o.prototype){ if(o.host != nil && o.host != me) return 0; for(i := 0; i < len o.props; i++) if(o.props[i] != nil && o.props[i].name == prop) return 1; } return 0;} esdelete(ex: ref Exec, o: ref Obj, prop: string, force: int){ if(!force && o.host != nil && o.host != me) return o.host->delete(ex, o, prop); for(i := 0; i < len o.props; i++){ if(o.props[i] != nil && o.props[i].name == prop){ if(!(o.props[i].attr & DontDelete)) o.props[i] = nil; return; } }} esdeforder := array[] of {"valueOf", "toString"};esdefaultval(ex: ref Exec, o: ref Obj, ty: int, force: int): ref Val{ v: ref Val; if(!force && o.host != nil && o.host != me){ v = o.host->defaultval(ex, o, ty); valcheck(ex, v, NoHint); if(!isprimval(v)) runtime(ex, TypeError, "host object returned an object to [[DefaultValue]]"); return v; } hintstr := 0; if(ty == TStr || ty == NoHint && isdateobj(o)) hintstr = 1; for(i := 0; i < 2; i++){ v = esget(ex, o, esdeforder[hintstr ^ i], 0); if(v != undefined && v.ty == TObj && v.obj.call != nil){ r := escall(ex, v.obj, o, nil, 0); v = nil; if(!r.isref) v = r.val; if(v != nil && isprimval(v)) return v; } } runtime(ex, TypeError, "no default value"); return nil;} esprimid(ex: ref Exec, s: string): ref Ref{ for(sc := ex.scopechain; sc != nil; sc = tl sc){ o := hd sc; if(eshasproperty(ex, o, s, 0) == true) return ref Ref(1, nil, o, s); } # # the right place to add literals? # case s{ "null" => return ref Ref(0, null, nil, "null"); "true" => return ref Ref(0, true, nil, "true"); "false" => return ref Ref(0, false, nil, "false"); } return ref Ref(1, nil, nil, s);} bivar(ex: ref Exec, sc: list of ref Obj, s: string): ref Val{ for(; sc != nil; sc = tl sc){ o := hd sc; if(eshasproperty(ex, o, s, 0) == true) return esget(ex, o, s, 0); } return nil;} esconstruct(ex: ref Exec, func: ref Obj, args: array of ref Val): ref Obj{ o: ref Obj; if(func.construct == nil) runtime(ex, TypeError, "new must be applied to a constructor object"); if(func.host != nil) o = func.host->construct(ex, func, args); else{ o = getobj(esget(ex, func, "prototype", 0)); if(o == nil) o = ex.objproto; this := mkobj(o, "Object"); o = getobj(getValue(ex, escall(ex, func, this, args, 0))); # Divergence from ECMA-262 # # observed that not all script-defined constructors return an object, # the value of 'this' is assumed to be the value of the constructor if (o == nil) o = this; } if(o == nil) runtime(ex, TypeError, func.val.str+" failed to generate an object"); return o;} escall(ex: ref Exec, func, this: ref Obj, args: array of ref Val, eval: int): ref Ref{ if(func.call == nil) runtime(ex, TypeError, "can only call function objects"); if(this == nil) this = ex.global; r: ref Ref = nil; if(func.host != nil){ r = func.host->call(ex, func, this, args, 0); if(r.isref && r.name == nil) runtime(ex, ReferenceError, "host call returned a bad reference"); else if(!r.isref) valcheck(ex, r.val, NoHint); return r; } argobj := mkobj(ex.objproto, "Object"); actobj := mkobj(nil, "Activation"); oargs: ref RefVal = nil; props := func.props; empty := -1; i := 0; for(i = 0; i < len props; i++){ if(props[i] == nil) empty = i; else if(props[i].name == "arguments"){ oargs = props[i].val; empty = i; break; } } if(i == len func.props){ if(empty == -1){ props = array[i+1] of ref Prop; props[:] = func.props; func.props = props; empty = i; } props[empty] = ref Prop(DontDelete|DontEnum|ReadOnly, "arguments", nil); } props[empty].val = ref RefVal(objval(argobj)); # #see section 10.1.3 page 33 # if multiple params share the same name, the last one takes effect # vars don't override params of the same name, or earlier parms defs # actobj.props = array[] of {ref Prop(DontDelete, "arguments", ref RefVal(objval(argobj)))}; argobj.props = array[len args + 2] of { ref Prop(DontEnum, "callee", ref RefVal(objval(func))), ref Prop(DontEnum, "length", ref RefVal(numval(real len args))), }; # # instantiate the arguments by name in the activation object # and by number in the arguments object, aliased to the same RefVal. # params := func.call.params; for(i = 0; i < len args; i++){ rjv := ref RefVal(args[i]); argobj.props[i+2] = ref Prop(DontEnum, string i, rjv);Lines 1–500 of 836. Load more
# # want to use the value in a context which # prefers an object, so coerce schizo vals # to object versions # coerceToObj(ex: ref Exec, v: ref Val): ref Val { o: ref Obj; case v.ty{ TBool => o = mkobj(ex.boolproto, "Boolean"); o.val = v; TStr => o = mkobj(ex.strproto, "String"); o.val = v; valinstant(o, DontEnum|DontDelete|ReadOnly, "length", numval(real len v.str)); TNum => o = mkobj(ex.numproto, "Number"); o.val = v; TRegExp => o = mkobj(ex.regexpproto, "RegExp"); o.val = v; valinstant(o, DontEnum|DontDelete|ReadOnly, "length", numval(real len v.rev.p)); valinstant(o, DontEnum|DontDelete|ReadOnly, "source", strval(v.rev.p)); valinstant(o, DontEnum|DontDelete|ReadOnly, "global", strhas(v.rev.f, 'g')); valinstant(o, DontEnum|DontDelete|ReadOnly, "ignoreCase", strhas(v.rev.f, 'i')); valinstant(o, DontEnum|DontDelete|ReadOnly, "multiline", strhas(v.rev.f, 'm')); valinstant(o, DontEnum|DontDelete, "lastIndex", numval(real v.rev.i)); * => return v; } return objval(o); } coerceToVal(v: ref Val): ref Val { if(v.ty != TObj) return v; o := v.obj; if(o.host != nil && o.host != me || o.class != "String" || o.class != "Number" || o.class != "Boolean") return v; return o.val; } isstrobj(o: ref Obj): int { return (o.host == nil || o.host == me) && o.class == "String"; } isnumobj(o: ref Obj): int { return (o.host == nil || o.host == me) && o.class == "Number"; } isboolobj(o: ref Obj): int { return (o.host == nil || o.host == me) && o.class == "Boolean"; } isdateobj(o: ref Obj): int { return (o.host == nil || o.host == me) && o.class == "Date"; } isregexpobj(o: ref Obj): int { return (o.host == nil || o.host == me) && o.class == "RegExp"; } isfuncobj(o: ref Obj): int { return (o.host == nil || o.host == me) && o.class == "Function"; } isarray(o: ref Obj): int { # return (o.host == nil || o.host == me) && o.class == "Array"; # relax the host test # so that hosts can intercept Array operations and defer # unhandled ops to the builtin return o.class == "Array"; } iserr(o: ref Obj): int { return (o.host == nil || o.host == me) && o.class == "Error"; } isactobj(o: ref Obj): int { return o.host == nil && o.class == "Activation"; } isnull(v: ref Val): int { return v == null || v == nil; } isundefined(v: ref Val): int { return v == undefined; } isstr(v: ref Val): int { return v.ty == TStr; } isnum(v: ref Val): int { return v.ty == TNum; } isbool(v: ref Val): int { return v.ty == TBool; } isobj(v: ref Val): int { return v.ty == TObj; } isregexp(v: ref Val): int { return v.ty == TRegExp || v.ty == TObj && isregexpobj(v.obj); } # # retrieve the object field if it's valid # getobj(v: ref Val): ref Obj { if(v.ty == TObj) return v.obj; return nil; } isprimval(v: ref Val): int { return v.ty != TObj; } pushscope(ex: ref Exec, o: ref Obj) { ex.scopechain = o :: ex.scopechain; } popscope(ex: ref Exec) { ex.scopechain = tl ex.scopechain; } runtime(ex: ref Exec, o: ref Obj, s: string) { ex.error = s; if(o == nil) ex.errval = undefined; else ex.errval = objval(o); if(debug['r']){ print("ecmascript runtime error: %s\n", s); if(""[5] == -1); # abort } raise "throw"; exit; # never reached } mkobj(proto: ref Obj, class: string): ref Obj { if(class == nil) class = "Object"; return ref Obj(nil, proto, nil, nil, nil, class, nil, nil); } valcheck(ex: ref Exec, v: ref Val, hint: int) { if(v == nil || v.ty < 0 || v.ty >= NoHint || v.ty == TBool && v != true && v != false || v.ty == TObj && v.obj == nil || hint != NoHint && v.ty != hint) runtime(ex, RangeError, "bad value generated by host object"); } # builtin methods for properties esget(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val { for( ; o != nil; o = o.prototype){ if(!force && o.host != nil && o.host != me){ v := o.host->get(ex, o, prop); valcheck(ex, v, NoHint); return v; } for(i := 0; i < len o.props; i++) if(o.props[i] != nil && o.props[i].name == prop) return o.props[i].val.val; force = 0; } return undefined; } esputind(o: ref Obj, prop: string): int { empty := -1; props := o.props; for(i := 0; i < len props; i++){ if(props[i] == nil) empty = i; else if(props[i].name == prop) return i; } if(empty != -1) return empty; props = array[i+1] of ref Prop; props[:] = o.props; o.props = props; return i; } esput(ex: ref Exec, o: ref Obj, prop: string, v: ref Val, force: int) { ai: big; if(!force && o.host != nil && o.host != me) return o.host->put(ex, o, prop, v); if(escanput(ex, o, prop, 0) != true) return; # # should this test for prototype == ex.arrayproto? # hard to say, but 15.4.5 "Properties of Array Instances" implies not # if(isarray(o)) al := toUint32(ex, esget(ex, o, "length", 1)); i := esputind(o, prop); props := o.props; if(props[i] != nil) props[i].val.val = v; else props[i] = ref Prop(0, prop, ref RefVal(v)); if(!isarray(o)) return; if(prop == "length"){ nl := toUint32(ex, v); for(ai = nl; ai < al; ai++) esdelete(ex, o, string ai, 1); props[i].val.val = numval(real nl); }else{ ai = big prop; if(prop != string ai || ai < big 0 || ai >= 16rffffffff) return; i = esputind(o, "length"); if(props[i] == nil) fatal(ex, "bogus array esput"); else if(toUint32(ex, props[i].val.val) <= ai) props[i].val.val = numval(real(ai+big 1)); } } escanput(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val { for( ; o != nil; o = o.prototype){ if(!force && o.host != nil && o.host != me){ v := o.host->canput(ex, o, prop); valcheck(ex, v, TBool); return v; } for(i := 0; i < len o.props; i++){ if(o.props[i] != nil && o.props[i].name == prop){ if(o.props[i].attr & ReadOnly) return false; else return true; } } force = 0; } return true; } eshasproperty(ex: ref Exec, o: ref Obj, prop: string, force: int): ref Val { for(; o != nil; o = o.prototype){ if(!force && o.host != nil && o.host != me){ v := o.host->hasproperty(ex, o, prop); valcheck(ex, v, TBool); return v; } for(i := 0; i < len o.props; i++) if(o.props[i] != nil && o.props[i].name == prop) return true; } return false; } eshasenumprop(o: ref Obj, prop: string): ref Val { for(i := 0; i < len o.props; i++) if(o.props[i] != nil && o.props[i].name == prop){ if(o.props[i].attr & DontEnum) return false; return true; } return false; } propshadowed(start, end: ref Obj, prop: string): int { for(o := start; o != end; o = o.prototype){ if(o.host != nil && o.host != me) return 0; for(i := 0; i < len o.props; i++) if(o.props[i] != nil && o.props[i].name == prop) return 1; } return 0; } esdelete(ex: ref Exec, o: ref Obj, prop: string, force: int) { if(!force && o.host != nil && o.host != me) return o.host->delete(ex, o, prop); for(i := 0; i < len o.props; i++){ if(o.props[i] != nil && o.props[i].name == prop){ if(!(o.props[i].attr & DontDelete)) o.props[i] = nil; return; } } } esdeforder := array[] of {"valueOf", "toString"}; esdefaultval(ex: ref Exec, o: ref Obj, ty: int, force: int): ref Val { v: ref Val; if(!force && o.host != nil && o.host != me){ v = o.host->defaultval(ex, o, ty); valcheck(ex, v, NoHint); if(!isprimval(v)) runtime(ex, TypeError, "host object returned an object to [[DefaultValue]]"); return v; } hintstr := 0; if(ty == TStr || ty == NoHint && isdateobj(o)) hintstr = 1; for(i := 0; i < 2; i++){ v = esget(ex, o, esdeforder[hintstr ^ i], 0); if(v != undefined && v.ty == TObj && v.obj.call != nil){ r := escall(ex, v.obj, o, nil, 0); v = nil; if(!r.isref) v = r.val; if(v != nil && isprimval(v)) return v; } } runtime(ex, TypeError, "no default value"); return nil; } esprimid(ex: ref Exec, s: string): ref Ref { for(sc := ex.scopechain; sc != nil; sc = tl sc){ o := hd sc; if(eshasproperty(ex, o, s, 0) == true) return ref Ref(1, nil, o, s); } # # the right place to add literals? # case s{ "null" => return ref Ref(0, null, nil, "null"); "true" => return ref Ref(0, true, nil, "true"); "false" => return ref Ref(0, false, nil, "false"); } return ref Ref(1, nil, nil, s); } bivar(ex: ref Exec, sc: list of ref Obj, s: string): ref Val { for(; sc != nil; sc = tl sc){ o := hd sc; if(eshasproperty(ex, o, s, 0) == true) return esget(ex, o, s, 0); } return nil; } esconstruct(ex: ref Exec, func: ref Obj, args: array of ref Val): ref Obj { o: ref Obj; if(func.construct == nil) runtime(ex, TypeError, "new must be applied to a constructor object"); if(func.host != nil) o = func.host->construct(ex, func, args); else{ o = getobj(esget(ex, func, "prototype", 0)); if(o == nil) o = ex.objproto; this := mkobj(o, "Object"); o = getobj(getValue(ex, escall(ex, func, this, args, 0))); # Divergence from ECMA-262 # # observed that not all script-defined constructors return an object, # the value of 'this' is assumed to be the value of the constructor if (o == nil) o = this; } if(o == nil) runtime(ex, TypeError, func.val.str+" failed to generate an object"); return o; } escall(ex: ref Exec, func, this: ref Obj, args: array of ref Val, eval: int): ref Ref { if(func.call == nil) runtime(ex, TypeError, "can only call function objects"); if(this == nil) this = ex.global; r: ref Ref = nil; if(func.host != nil){ r = func.host->call(ex, func, this, args, 0); if(r.isref && r.name == nil) runtime(ex, ReferenceError, "host call returned a bad reference"); else if(!r.isref) valcheck(ex, r.val, NoHint); return r; } argobj := mkobj(ex.objproto, "Object"); actobj := mkobj(nil, "Activation"); oargs: ref RefVal = nil; props := func.props; empty := -1; i := 0; for(i = 0; i < len props; i++){ if(props[i] == nil) empty = i; else if(props[i].name == "arguments"){ oargs = props[i].val; empty = i; break; } } if(i == len func.props){ if(empty == -1){ props = array[i+1] of ref Prop; props[:] = func.props; func.props = props; empty = i; } props[empty] = ref Prop(DontDelete|DontEnum|ReadOnly, "arguments", nil); } props[empty].val = ref RefVal(objval(argobj)); # #see section 10.1.3 page 33 # if multiple params share the same name, the last one takes effect # vars don't override params of the same name, or earlier parms defs # actobj.props = array[] of {ref Prop(DontDelete, "arguments", ref RefVal(objval(argobj)))}; argobj.props = array[len args + 2] of { ref Prop(DontEnum, "callee", ref RefVal(objval(func))), ref Prop(DontEnum, "length", ref RefVal(numval(real len args))), }; # # instantiate the arguments by name in the activation object # and by number in the arguments object, aliased to the same RefVal. # params := func.call.params; for(i = 0; i < len args; i++){ rjv := ref RefVal(args[i]); argobj.props[i+2] = ref Prop(DontEnum, string i, rjv);