forsyth20121205-174063a80cc13 years agoHistory
836 lines · 17.5 KB · Brainfuck
RawDownload
## 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.