=pod This file is the beginning of a simple JavaScript-on-p6 runtime. It seems likely there will be more than one. Design goals: 1 Correctness - mirror the spec. 2 Interoperation - allow blending of p6 and js objects. (Even though this impairs 1?) Please note efficiency isnt currently on the goal list. CAVEAT - This file so far represents only a an hour or two of work. It is very much in a state of flux. =cut class JSX::PseudoValue; class JSX::InternalValue is JSX::PseudoValue; class JSX::InternalValue::Reference is JSX::InternalValue; class JSX::InternalValue::List is JSX::InternalValue; class JSX::InternalValue::Completion is JSX::InternalValue; class JSX::Value is JSX::PseudoValue; class JSX::ValuePrimitive is JSX::Value; class JSX::Value::Undefined is JSX::ValuePrimitive; class JSX::Value::Null is JSX::ValuePrimitive; class JSX::Value::Boolean is JSX::ValuePrimitive; class JSX::Value::Number is JSX::ValuePrimitive; class JSX::Value::String is JSX::ValuePrimitive; class JSX::Value::Object is JSX::Value; class JSX::Value::Array is JSX::Value::Object; class JSX::PropertyAttributes { has $.ReadOnly; has $.DontEnum; has $.DontDelete; } class JSX::Value; has $.property_value; has $.property_attributes; # 8.6.2.1 method <[[Get]]> ($O: JSX::String $P) { my $Result2 = $O.property_value{$P}; return $Result2 if defined $Result2; my $proto = $O.<[[Prototype]]>; return JSX::undefined if $proto ~~ JSX::null; return $proto.<[[Get]]>($P); } # 8.6.2.2 method <[[Put]]> ($O: JSX::String $P, JSX::Value $V) { my $Result1 = $O.<[[CanPut]]>($P); return if $Result1 ~~ JSX::false; if $O.property_value.exists($P) { $O.property_value($P) = $V; return; } $O.property_value($P) = $V; return; } # 8.6.2.3 method <[[CanPut]]> ($O: JSX::String $P) { if $O.property_value.exists($P) { my $attr = $O.property_attributes{$P}; return JSX::false if $attr && $attr.ReadOnly; return JSX::true; } my $proto = $O.<[[Prototype]]>; return JSX::true if $proto ~~ JSX::null; return $proto.<[[CanPut]]>($P); } # 8.6.2.4 method <[[HasProperty]]> ($O: JSX::String $P) { return JSX::true if $O.property_value.exists($P); my $proto = $O.<[[Prototype]]>; return JSX::false if $proto ~~ JSX::null; return $proto.<[[CanPut]]>($P); } # 8.6.2.5 method <[[Delete]]> ($O: JSX::String $P) { return JSX::true if !$O.property_value.exists($P); my $attr = $O.property_attributes{$P}; return JSX::false if $attr && $attr.ReadOnly; delete $O.property_value{$P}; delete $O.property_attribute{$P}; return JSX::true; } # 8.6.2.6 multi method <[[DefaultValue]]> ($O: ?$hint = 'Number') { if $hint eq 'String' { my $Result1 = $O.<[[Get]]>('toString'); if $Result1.isa(JSX::Value::Object) { my $Result3 = JSX::with_this($O) { $Result1.<[[Get]]>() }; return $Result3 if $Result3.isa(JSX::ValuePrimitive); } # 5 my $Result5 = $O.<[[Get]]>('valueOf'); if $Result5.isa(JSX::Value::Object) { my $Result7 = JSX::with_this($O) { $Result1.<[[Get]]>() }; return $Result7 if $Result7.isa(JSX::ValuePrimitive); } # 9 raise TypeError; } elsif $hint eq 'Number' { my $Result1 = $O.<[[Get]]>('valueOf'); if $Result1.isa(JSX::Value::Object) { my $Result3 = JSX::with_this($O) { $Result1.<[[Get]]>() }; return $Result3 if $Result3.isa(JSX::ValuePrimitive); } # 5 my $Result5 = $O.<[[Get]]>('toString'); if $Result5.isa(JSX::Value::Object) { my $Result7 = JSX::with_this($O) { $Result1.<[[Get]]>() }; return $Result7 if $Result7.isa(JSX::ValuePrimitive); } # 9 raise TypeError; } else { die "bug - invalid hint $hint"; } } # 8.7 The Reference Type class JSX::InternalValue::Reference { has $.base_object; has $.property_name; method GetBase ($O:) { $.base_object } method GetPropertyName ($O:) { $.property_name } } # 8.7.1 method GetValue (JSX::PsuedoValue $V) { $V } method GetValue (JSX::InternalValue::Reference $V) { my $Result2 = GetBase($V); raise ReferenceError if $Result2 ~~ JSX::null; my $Result4 = $Result2.<[[Get]]>(GetPropertyName($V)); return $Result4; } # 8.7.2 method PutValue (JSX::PsuedoValue $V) { raise ReferenceError; } method PutValue (JSX::InternalValue::Reference $V, $W) { my $Result2 = GetBase($V); if !($Result2 ~~ JSX::null) { $Result2.<[[Put]]>(GetPropertyName($V),$W); return; } # 6 JSX::global_object.<[[Put]]>(GetPropertyName($V),$W); return; } # 8.8 The List Type class JSX::InternalValue::List is Array; # 8.9 The Completion Type class JSX::InternalValue::Completion_type { # XXX - should be enum has $.normal; has $.break; has $.continue; has $.return; has $.throw; } class JSX::InternalValue::Completion { submethod BUILD { $.type = JSX::InternalValue::Completion_type.new(); } has $.type; has $.value; has $.target; method is_abrupt_completion() { !$.type.normal } } # empty is represented by undef. # 9. Type Conversion # 9.1 multi sub ToPrimitive(JSX::Value::Object $o) {$o.<[[DefaultValue]]>} multi sub ToPrimitive(JSX::Value $v) {$v} # 9.2 multi sub ToBoolean(JSX::Value::Undefined $v) { JSX::false } multi sub ToBoolean(JSX::Value::Null $v) { JSX::false } multi sub ToBoolean(JSX::Value::Boolean $v) { $v } multi sub ToBoolean(JSX::Value::Number $v) { +$v == (+0|-0|NaN) ?? JSX::false !! JSX::true; } multi sub ToBoolean(JSX::Value::String $v) { ~$v eq "" ?? JSX::false !! JSX::true; } multi sub ToBoolean(JSX::Value::Object $v) { JSX::true } # 9.3 multi sub ToNumber(JSX::Value::Undefined $v) { NaN } multi sub ToNumber(JSX::Value::Null $v) { +0 } multi sub ToNumber(JSX::Value::Boolean $v) { ?$v :: JSX::true :: +0 } multi sub ToNumber(JSX::Value::Number $v) { $v } multi sub ToNumber(JSX::Value::String $v) { my $s = ~$v; return NaN if $s !~ rx/^$/; $s ~~ s:g/+//; $s ~~ s/^([\+|\-]?)0+(<[0..9]>)/$1$2/; return +0 if $s eq ""; return +$s; } multi sub ToNumber(JSX::Value::Object $v) { my $Result1 = ToPrimitive($v,'Number'); my $Result2 = ToNumber($Result1); return $Result2; } # 9.4 multi sub ToInteger(JSX::Value $v) { use Math qw(sign floor abs); my $Result1 = ToNumber($v); return +0 if $Result1 == NaN; return $Result1 if $Result1 == (+0|-0|Inf|-Inf); my $Result4 = sign($Result1) * floor(abs($Result1)); return $Result4; } # 9.5 multi sub ToInt32(JSX::Value $v) { use Math qw(sign floor abs); my $Result1 = ToNumber($v); return +0 if $Result1 == (NaN|+0|-0|Inf|-Inf); my $Result3 = sign($Result1) * floor(abs($Result1)); my $n2_32 = 2**32; my $n2_31 = 2**31; my $Result4 = $Result3 % $n2_32; return $Result4-$n2_32 if $Result4 >= $n2_31; return $Result4; } # 9.6 multi sub ToUint32(JSX::Value $v) { use Math qw(sign floor abs); my $Result1 = ToNumber($v); return +0 if $Result1 == (NaN|+0|-0|Inf|-Inf); my $Result3 = sign($Result1) * floor(abs($Result1)); my $n2_32 = 2**32; my $Result4 = $Result3 % $n2_32; return $Result4; } # 9.7 multi sub ToUint16(JSX::Value $v) { use Math qw(sign floor abs); my $Result1 = ToNumber($v); return +0 if $Result1 == (NaN|+0|-0|Inf|-Inf); my $Result3 = sign($Result1) * floor(abs($Result1)); my $n2_16 = 2**16; my $Result4 = $Result3 % $n2_16; return $Result4; } # 9.8 # 9.9 class JSX::Value::Array; submethod BUILD { $.property_attributes{'length'} = JSX::PropertyAttributes.new(:DontEnum :DontDelete); } # 15.4.5.1 method <[[Put]]> ($A: JSX::String $P, JSX::Value $V) { my $Result1 = $A.<[[CanPut]]>($P); return if $Result1 ~~ JSX::false; if $A.property_value.exists($P) { if $P eq 'length' { # 12-16 my $Result12 = ToUint32($V); raise RangeError if $Result12 != ToNumber($V); my $length = +($A.property_value{'length'}); my $k; loop(;$k < $length; $k++) { my $key = $k; # Eh. Spec would have us use ToString(k). $A.<[[Delete]]>($k) if exists $A.property_value{$key}; # XXX - need to wrap $k? } $A.property_value{$P} = $Result12; return; } # 5 $A.property_value($P) = $V; } else { # 7 $A.property_value($P) = $V; } # 8 my sub not_an_array_index($P){ !($P ~~ rx:perl5/\A\d+\Z/); }; return if not_an_array_index($P); my $uint = ToUint32($P); return if $uint < $A.property_value{'length'}; $A.property_value{'length'} = $uint + 1; #XXX dont forget + wrappers return; }