private JSExpression ComputeAddress( JSExpression pointer, JSExpression offsetInElements, JSExpression offsetInBytes, out TypeReference elementType ) { var pointerType = pointer.GetActualType(TypeSystem); elementType = pointerType.GetElementType(); var elementSize = WasmUtil.SizeOfType(elementType); if ( (offsetInElements != null) && // I'm so helpful :-))))) (offsetInBytes == null) ) { offsetInBytes = new JSBinaryOperatorExpression( JSOperator.Multiply, offsetInElements, JSLiteral.New(elementSize), pointerType ); } if (offsetInBytes != null) { var result = new JSPointerAddExpression(pointer, offsetInBytes, false); // Console.WriteLine("Constructing pae {0} from {1} {2} {3}", result, pointer, offsetInElements, offsetInBytes); return(result); } else { return(pointer); } }
public void NodeChildren () { var de = new JSDotExpression(JSLiteral.New(1), new JSStringIdentifier("2")); var boe = new JSBinaryOperatorExpression(JSOperator.Add, JSLiteral.New(1), JSLiteral.New(2), T1); Assert.AreEqual(2, de.Children.Count()); Assert.AreEqual(2, boe.Children.Count()); }
public static JSExpression[] GetArrayDimensions(TypeReference arrayType) { var at = arrayType as ArrayType; if (at == null) { return(null); } var result = new List <JSExpression>(); for (var i = 0; i < at.Dimensions.Count; i++) { var dim = at.Dimensions[i]; if (dim.IsSized) { result.Add(JSLiteral.New(dim.UpperBound.Value)); } else { return(null); } } return(result.ToArray()); }
private static JSExpression Mul(TypeSystem typeSystem, JSExpression lhs, JSExpression rhs) { long iLhs, iRhs; if (ExtractLiteral(lhs, out iLhs) && ExtractLiteral(rhs, out iRhs)) { return(JSLiteral.New((int)(iLhs * iRhs))); } return(new JSBinaryOperatorExpression(JSOperator.Multiply, lhs, rhs, typeSystem.Int32)); }
public JSExpression TranslateAttributeConstructorArgument( TypeSystem typeSystem, TypeReference context, CustomAttributeArgument ca ) { if (ca.Value == null) { return(JSLiteral.Null(ca.Type)); } else if (ca.Value is CustomAttributeArgument) { // :| return(TranslateAttributeConstructorArgument( typeSystem, context, (CustomAttributeArgument)ca.Value )); } else if (ca.Value is CustomAttributeArgument[]) { // Issue #141. WTF. var valueArray = (CustomAttributeArgument[])ca.Value; return(new JSArrayExpression(typeSystem.Object, (from value in valueArray select TranslateAttributeConstructorArgument( typeSystem, context, value )).ToArray() )); } else if (ca.Type.FullName == "System.Type") { return(new JSTypeOfExpression((TypeReference)ca.Value)); } else if (TypeUtil.IsEnum(ca.Type)) { var longValue = Convert.ToInt64(ca.Value); var result = JSEnumLiteral.TryCreate( _TypeInfoProvider.GetExisting(ca.Type), longValue ); if (result != null) { return(result); } else { return(JSLiteral.New(longValue)); } } else { try { return(JSLiteral.New(ca.Value as dynamic)); } catch (Exception) { throw new NotImplementedException(String.Format("Attribute arguments of type '{0}' are not implemented.", ca.Type.FullName)); } } }
private static JSExpression Add(TypeSystem typeSystem, JSExpression lhs, JSExpression rhs) { long iLhs, iRhs; if (ExtractLiteral(lhs, out iLhs) && ExtractLiteral(rhs, out iRhs)) { return(JSLiteral.New((int)(iLhs + iRhs))); } var type = lhs.GetActualType(typeSystem); return(new JSBinaryOperatorExpression(JSOperator.Add, lhs, rhs, type)); }
private JSBinaryOperatorExpression MakeUnaryMutation( JSExpression expressionToMutate, JSBinaryOperator mutationOperator, TypeReference type ) { var newValue = new JSBinaryOperatorExpression( mutationOperator, expressionToMutate, JSLiteral.New(1), type ); var assignment = new JSBinaryOperatorExpression( JSOperator.Assignment, expressionToMutate, newValue, type ); return(assignment); }
public void NewArrayTraversal () { var na = new JSNewArrayExpression( T1, new JSExpression[] { JSLiteral.New(2) }, new JSArrayExpression( T1, JSLiteral.New(3), JSLiteral.New(4) ) ); // DumpNodeSequence(na.SelfAndChildrenRecursive); DumpNodeSequence(na.AllChildrenRecursive); Assert.AreEqual(2, na.Children.ToArray().Length, "Child count"); Assert.AreEqual(4, na.AllChildrenRecursive.ToArray().Length, "Recursive child count"); }
public static JSBinaryOperatorExpression MakeUnaryMutation( JSExpression expressionToMutate, JSBinaryOperator mutationOperator, TypeReference type, TypeSystem typeSystem ) { var newValue = new JSBinaryOperatorExpression( mutationOperator, expressionToMutate, JSLiteral.New(1), type ); var assignment = new JSBinaryOperatorExpression( JSOperator.Assignment, MakeLhsForAssignment(expressionToMutate), newValue, type ); assignment = ConvertReadExpressionToWriteExpression(assignment, typeSystem); return(assignment); }
public void VisitNode(JSUnaryOperatorExpression uoe) { JSUnaryMutationOperator op; JSExpression target; TypeReference type; if (UnpackUnaryMutation(uoe, out op, out target, out type)) { var tempVar = TemporaryVariable.ForFunction( Stack.Last() as JSFunctionExpression, type ); var store = new JSBinaryOperatorExpression( JSOperator.Assignment, tempVar, target, type ); var delta = ( (op == JSOperator.PostIncrement) || (op == JSOperator.PreIncrement) ) ? 1 : -1; JSExpression replacement; if ( (op == JSOperator.PostIncrement) || (op == JSOperator.PostDecrement) ) { var mutated = new JSPointerAddExpression(target, JSLiteral.New(delta), false); replacement = new JSCommaExpression(store, mutated, tempVar); } else { replacement = new JSPointerAddExpression(target, JSLiteral.New(delta), true); } ParentNode.ReplaceChild(uoe, replacement); VisitReplacement(replacement); return; } VisitChildren(uoe); }
protected JSInvocationExpression CastToInteger(JSExpression charExpression) { return(JSInvocationExpression.InvokeMethod( JS.charCodeAt, charExpression, new[] { JSLiteral.New(0) }, true )); }
public void VisitNode(JSBinaryOperatorExpression boe) { var leftType = boe.Left.GetActualType(TypeSystem); var rightType = boe.Right.GetActualType(TypeSystem); // We can end up with a pointer literal in an arithmetic expression. // In this case we want to switch it back to a normal integer literal so that the math operations work. var leftPointer = boe.Left as JSPointerLiteral; var rightPointer = boe.Right as JSPointerLiteral; if (!(boe.Operator is JSAssignmentOperator)) { if (leftPointer != null) { boe.ReplaceChild(boe.Left, JSLiteral.New(leftPointer.Value)); } if (rightPointer != null) { boe.ReplaceChild(boe.Right, JSLiteral.New(rightPointer.Value)); } } JSExpression replacement = null; if (leftType.IsPointer && TypeUtil.IsIntegral(rightType)) { if ( (boe.Operator == JSOperator.Add) || (boe.Operator == JSOperator.AddAssignment) ) { replacement = new JSPointerAddExpression( boe.Left, boe.Right, boe.Operator == JSOperator.AddAssignment ); } else if ( (boe.Operator == JSOperator.Subtract) || (boe.Operator == JSOperator.SubtractAssignment) ) { // FIXME: Int32 is probably wrong replacement = new JSPointerAddExpression( boe.Left, new JSUnaryOperatorExpression(JSOperator.Negation, boe.Right, TypeSystem.Int32), boe.Operator == JSOperator.SubtractAssignment ); } } else if (leftType.IsPointer && rightType.IsPointer) { if (boe.Operator == JSOperator.Subtract) { // FIXME: Int32 is probably wrong replacement = new JSPointerDeltaExpression( boe.Left, boe.Right, TypeSystem.Int32 ); } else if (boe.Operator is JSComparisonOperator) { replacement = new JSPointerComparisonExpression(boe.Operator, boe.Left, boe.Right, boe.ActualType); } } if (replacement != null) { ParentNode.ReplaceChild(boe, replacement); VisitReplacement(replacement); } else { VisitChildren(boe); } }
private void EmitFieldIntrinsics(int heapSize) { // FIXME: Gross var tis = (ITypeInfoSource)Translator.TypeInfoProvider; Formatter.WriteRaw(";; Compiler-generated field accessors"); Formatter.NewLine(); foreach (var kvp in FieldTable.OrderBy(kvp => kvp.Value.Offset)) { var fd = kvp.Value.Field; var fi = (FieldInfo)tis.Get(fd); var name = WasmUtil.FormatMemberName(fi.Member); var typeSystem = fd.FieldType.Module.TypeSystem; // HACK var baseAddressParam = new JSVariable("address", typeSystem.Int32, null); // HACK var valueParam = new JSVariable("value", fd.FieldType, null); JSExpression address; if (fd.IsStatic) { address = JSLiteral.New(kvp.Value.Offset + heapSize); } else { address = new JSBinaryOperatorExpression( JSOperator.Add, baseAddressParam, JSLiteral.New(kvp.Value.Offset), typeSystem.Int32 ); } Formatter.ConditionalNewLine(); Formatter.WriteRaw( "(func $__get_{0} (result {1}){2}(return ", name, WasmUtil.PickTypeKeyword(fd.FieldType), fd.IsStatic ? " " : " (param $address i32) " ); var gm = new GetMemory( fd.FieldType, /* FIXME: Align addresses */ false, address ); // HACK EntryPointAstEmitter.Emit(gm); Formatter.WriteRaw(") )"); if (fd.IsInitOnly) { continue; } Formatter.NewLine(); Formatter.WriteRaw( "(func $__set_{0}{2}(param $value {1}) ", name, WasmUtil.PickTypeKeyword(fd.FieldType), fd.IsStatic ? " " : " (param $address i32) " ); Formatter.Indent(); Formatter.NewLine(); var sm = new SetMemory( fd.FieldType, /* FIXME: Align addresses */ false, address, valueParam ); // HACK EntryPointAstEmitter.Emit(sm); Formatter.Unindent(); Formatter.ConditionalNewLine(); Formatter.WriteRaw(")"); } Formatter.NewLine(); Formatter.NewLine(); }
private JSForLoop ReplaceWhileLoopAndEnumerator(JSWhileLoop wl, JSExpression backingStore, JSExpression enumerator, TypeInfo enumeratorType, string arrayMember, string lengthMember) { var loopId = _NextLoopId++; var arrayVariableName = String.Format("a${0:x}", loopId); var indexVariableName = String.Format("i${0:x}", loopId); var lengthVariableName = String.Format("l${0:x}", loopId); var currentPropertyReference = enumeratorType.Definition.Properties.First((p) => p.Name == "Current"); var currentPropertyInfo = enumeratorType.Source.GetProperty(currentPropertyReference); var itemType = currentPropertyInfo.ReturnType; var arrayType = new ArrayType(itemType); var arrayVariable = new JSVariable( arrayVariableName, arrayType, Function.Method.Reference, JSDotExpression.New(backingStore, new JSStringIdentifier(arrayMember, arrayType)) ); var indexVariable = new JSVariable( indexVariableName, TypeSystem.Int32, Function.Method.Reference, JSLiteral.New(0) ); var lengthVariable = new JSVariable( lengthVariableName, TypeSystem.Int32, Function.Method.Reference, JSDotExpression.New(backingStore, new JSStringIdentifier(lengthMember, TypeSystem.Int32)) ); var initializer = new JSVariableDeclarationStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, arrayVariable, arrayVariable.DefaultValue, arrayVariable.IdentifierType ), new JSBinaryOperatorExpression( JSOperator.Assignment, indexVariable, indexVariable.DefaultValue, indexVariable.IdentifierType ), new JSBinaryOperatorExpression( JSOperator.Assignment, lengthVariable, lengthVariable.DefaultValue, lengthVariable.IdentifierType ) ); var condition = new JSBinaryOperatorExpression( JSOperator.LessThan, indexVariable, lengthVariable, TypeSystem.Boolean ); var increment = new JSUnaryOperatorExpression( JSOperator.PostIncrement, indexVariable, TypeSystem.Int32 ); var result = new JSForLoop( initializer, condition, new JSExpressionStatement(increment), wl.Statements.ToArray() ); result.Index = wl.Index; new PropertyAccessReplacer( enumerator, new JSProperty(currentPropertyReference, currentPropertyInfo), new JSIndexerExpression( arrayVariable, indexVariable, itemType ) ).Visit(result); return(result); }
public void VisitNode(JSCastExpression ce) { var currentType = ce.Expression.GetActualType(TypeSystem); var targetType = ce.NewType; JSExpression newExpression = null; if (targetType.MetadataType == MetadataType.Char) { newExpression = JSInvocationExpression.InvokeStatic( JS.fromCharCode, new[] { ce.Expression }, true ); } else if ( (currentType.MetadataType == MetadataType.Char) && TypeUtil.IsIntegral(targetType) ) { newExpression = JSInvocationExpression.InvokeMethod( JS.charCodeAt, ce.Expression, new[] { JSLiteral.New(0) }, true ); } else if ( IntroduceEnumCasts.IsEnumOrNullableEnum(currentType) ) { var enumInfo = TypeInfo.Get(currentType); if (targetType.MetadataType == MetadataType.Boolean) { EnumMemberInfo enumMember; if (enumInfo.ValueToEnumMember.TryGetValue(0, out enumMember)) { newExpression = new JSBinaryOperatorExpression( JSOperator.NotEqual, ce.Expression, new JSEnumLiteral(enumMember.Value, enumMember), TypeSystem.Boolean ); } else if (enumInfo.ValueToEnumMember.TryGetValue(1, out enumMember)) { newExpression = new JSBinaryOperatorExpression( JSOperator.Equal, ce.Expression, new JSEnumLiteral(enumMember.Value, enumMember), TypeSystem.Boolean ); } else { newExpression = new JSUntranslatableExpression(String.Format( "Could not cast enum of type '{0}' to boolean because it has no zero value or one value", currentType.FullName )); } } else if (TypeUtil.IsNumeric(targetType)) { newExpression = JSInvocationExpression.InvokeStatic( JS.Number(targetType), new[] { ce.Expression }, true ); } else if (targetType.FullName == "System.Enum") { newExpression = ce.Expression; } else { // Debugger.Break(); } } else if ( targetType.MetadataType == MetadataType.Boolean ) { newExpression = new JSBinaryOperatorExpression( JSBinaryOperator.NotEqual, ce.Expression, new JSDefaultValueLiteral(currentType), TypeSystem.Boolean ); } else if ( TypeUtil.IsNumeric(targetType) && TypeUtil.IsNumeric(currentType) ) { if ( TypeUtil.IsIntegral(currentType) || !TypeUtil.IsIntegral(targetType) ) { newExpression = ce.Expression; } else { newExpression = JSInvocationExpression.InvokeStatic(JS.floor, new[] { ce.Expression }, true); } } else { newExpression = JSIL.Cast(ce.Expression, targetType); } if (newExpression != null) { ParentNode.ReplaceChild(ce, newExpression); VisitReplacement(newExpression); } else { // Debugger.Break(); VisitChildren(ce); } }
public void VisitNode(JSInvocationExpression ie) { var type = ie.JSType; var method = ie.JSMethod; var thisExpression = ie.ThisReference; if (method != null) { if ( (type != null) && (type.Type.FullName == "System.Object") ) { switch (method.Method.Member.Name) { case ".ctor": { var replacement = new JSNullExpression(); ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } case "GetType": { JSNode replacement; var thisType = JSExpression.DeReferenceType(thisExpression.GetActualType(TypeSystem), false); if ((thisType is GenericInstanceType) && thisType.FullName.StartsWith("System.Nullable")) { replacement = new JSType(thisType); } else { replacement = JSIL.GetTypeOf(thisExpression); } ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } } } else if ( IsNullable(type.Type) ) { var t = (type.Type as GenericInstanceType).GenericArguments[0]; var @null = JSLiteral.Null(t); var @default = new JSDefaultValueLiteral(t); switch (method.Method.Member.Name) { case ".ctor": JSExpression value; if (ie.Arguments.Count == 0) { value = @null; } else { value = ie.Arguments[0]; } var boe = new JSBinaryOperatorExpression( JSOperator.Assignment, ie.ThisReference, value, type.Type ); ParentNode.ReplaceChild(ie, boe); VisitReplacement(boe); break; case "GetValueOrDefault": var isNull = new JSBinaryOperatorExpression( JSOperator.Equal, ie.ThisReference, @null, TypeSystem.Boolean ); JSTernaryOperatorExpression ternary; if (ie.Arguments.Count == 0) { ternary = new JSTernaryOperatorExpression( isNull, @default, ie.ThisReference, type.Type ); } else { ternary = new JSTernaryOperatorExpression( isNull, ie.Arguments[0], ie.ThisReference, type.Type ); } ParentNode.ReplaceChild(ie, ternary); VisitReplacement(ternary); break; default: throw new NotImplementedException(method.Method.Member.FullName); } return; } else if ( (type != null) && TypeUtil.TypesAreEqual(TypeSystem.String, type.Type) && (method.Method.Name == "Concat") ) { if (ie.Arguments.Count > 2) { if (ie.Arguments.All( (arg) => TypeUtil.TypesAreEqual( TypeSystem.String, arg.GetActualType(TypeSystem) ) )) { var boe = JSBinaryOperatorExpression.New( JSOperator.Add, ie.Arguments, TypeSystem.String ); ParentNode.ReplaceChild( ie, boe ); VisitReplacement(boe); } } else if ( ie.Arguments.Count == 2 ) { var lhs = ie.Arguments[0]; var lhsType = TypeUtil.DereferenceType(lhs.GetActualType(TypeSystem)); if (!( TypeUtil.TypesAreEqual(TypeSystem.String, lhsType) || TypeUtil.TypesAreEqual(TypeSystem.Char, lhsType) )) { lhs = JSInvocationExpression.InvokeMethod(lhsType, JS.toString, lhs, null); } var rhs = ie.Arguments[1]; var rhsType = TypeUtil.DereferenceType(rhs.GetActualType(TypeSystem)); if (!( TypeUtil.TypesAreEqual(TypeSystem.String, rhsType) || TypeUtil.TypesAreEqual(TypeSystem.Char, rhsType) )) { rhs = JSInvocationExpression.InvokeMethod(rhsType, JS.toString, rhs, null); } var boe = new JSBinaryOperatorExpression( JSOperator.Add, lhs, rhs, TypeSystem.String ); ParentNode.ReplaceChild( ie, boe ); VisitReplacement(boe); } else if ( TypeUtil.GetTypeDefinition(ie.Arguments[0].GetActualType(TypeSystem)).FullName == "System.Array" ) { } else { var firstArg = ie.Arguments.FirstOrDefault(); ParentNode.ReplaceChild( ie, firstArg ); if (firstArg != null) { VisitReplacement(firstArg); } } return; } else if ( TypeUtil.IsDelegateType(method.Reference.DeclaringType) && (method.Method.Name == "Invoke") ) { var newIe = new JSDelegateInvocationExpression( thisExpression, ie.GetActualType(TypeSystem), ie.Arguments.ToArray() ); ParentNode.ReplaceChild(ie, newIe); VisitReplacement(newIe); return; } else if ( (method.Reference.DeclaringType.FullName == "System.Type") && (method.Method.Name == "GetTypeFromHandle") ) { var typeObj = ie.Arguments.FirstOrDefault(); ParentNode.ReplaceChild(ie, typeObj); VisitReplacement(typeObj); return; } else if ( (method.Reference.DeclaringType.Name == "RuntimeHelpers") && (method.Method.Name == "InitializeArray") ) { var array = ie.Arguments[0]; var arrayType = array.GetActualType(TypeSystem); var field = ie.Arguments[1].SelfAndChildrenRecursive.OfType <JSField>().First(); var initializer = JSArrayExpression.UnpackArrayInitializer(arrayType, field.Field.Member.InitialValue); var copy = JSIL.ShallowCopy(array, initializer, arrayType); ParentNode.ReplaceChild(ie, copy); VisitReplacement(copy); return; } else if ( method.Reference.DeclaringType.FullName == "System.Reflection.Assembly" ) { switch (method.Reference.Name) { case "GetExecutingAssembly": { var assembly = Method.DeclaringType.Module.Assembly; var asmNode = new JSAssembly(assembly); ParentNode.ReplaceChild(ie, asmNode); VisitReplacement(asmNode); return; } case "GetType": { switch (method.Method.Parameters.Length) { case 1: case 2: JSExpression throwOnFail = new JSBooleanLiteral(false); if (method.Method.Parameters.Length == 2) { throwOnFail = ie.Arguments[1]; } var invocation = JSIL.GetTypeFromAssembly( ie.ThisReference, ie.Arguments[0], throwOnFail ); ParentNode.ReplaceChild(ie, invocation); VisitReplacement(invocation); return; } break; } } } else if ( method.Method.DeclaringType.Definition.FullName == "System.Array" && (ie.Arguments.Count == 1) ) { switch (method.Method.Name) { case "GetLength": case "GetUpperBound": { var index = ie.Arguments[0] as JSLiteral; if (index != null) { var newDot = JSDotExpression.New(thisExpression, new JSStringIdentifier( String.Format("length{0}", Convert.ToInt32(index.Literal)), TypeSystem.Int32 )); if (method.Method.Name == "GetUpperBound") { var newExpr = new JSBinaryOperatorExpression( JSOperator.Subtract, newDot, JSLiteral.New(1), TypeSystem.Int32 ); ParentNode.ReplaceChild(ie, newExpr); } else { ParentNode.ReplaceChild(ie, newDot); } } break; } case "GetLowerBound": ParentNode.ReplaceChild(ie, JSLiteral.New(0)); break; } } } VisitChildren(ie); }
public JSExpression MaybeReplaceMethodCall(MethodReference caller, MethodReference method, MethodInfo methodInfo, JSExpression thisExpression, JSExpression[] arguments, TypeReference resultType, bool explicitThis) { var typeSystem = method.Module.TypeSystem; var fullName = method.FullName; switch (fullName) { case "System.Double System.Math::Sqrt(System.Double)": return(new AbstractSExpression( "f64.sqrt", typeSystem.Double, arguments, isConstantIfArgumentsAre: true )); case "System.Double System.Math::Floor(System.Double)": return(new AbstractSExpression( "f64.floor", typeSystem.Double, arguments, isConstantIfArgumentsAre: true )); case "System.Double System.Math::Ceiling(System.Double)": return(new AbstractSExpression( "f64.ceil", typeSystem.Double, arguments, isConstantIfArgumentsAre: true )); case "System.Void Wasm.Test::Printf(System.String,System.Object[])": // HACK: Ignored for now return(new JSNullExpression()); case "System.Void Wasm.Test::Invoke(System.String,System.Object[])": { var literalName = (JSStringLiteral)arguments[0]; var argumentValues = UnpackArgsArray(arguments[1]); return(new InvokeExport( literalName.Value, argumentValues )); } case "System.Void Wasm.Test::AssertReturn(System.Object,System.String,System.Object[])": { var expected = arguments[0]; string methodName; if (!ExtractLiteral(arguments[1], out methodName)) { throw new Exception("Expected export name as arg1 of assertreturn"); } var invokeArguments = UnpackArgsArray(arguments[2]); return(new AssertReturn(expected, methodName, invokeArguments)); } case "System.Void Wasm.Heap::SetHeapSize(System.Int32)": { var td = caller.DeclaringType.Resolve(); var hs = WasmUtil.HeapSizes; if (hs.ContainsKey(td)) { throw new Exception("Heap size for type " + td.FullName + " already set"); } long heapSize; if (!ExtractLiteral(arguments[0], out heapSize)) { throw new ArgumentException("SetHeapSize's argument must be an int literal"); } hs.Add(td, (int)heapSize); return(new JSNullExpression()); } case "System.Int32 Wasm.HeapI32::get_Item(System.Int32)": case "System.Int32 Wasm.HeapI32::get_Item(System.Int32,System.Int32)": { JSExpression actualAddress; if (arguments.Length == 2) { actualAddress = Add(typeSystem, arguments[0], arguments[1]); } else { actualAddress = arguments[0]; } // HACK: Indices are in elements, not bytes var actualAddressBytes = Mul(typeSystem, actualAddress, JSLiteral.New(4)); return(new GetMemory(typeSystem.Int32, false, actualAddressBytes)); } case "System.Byte Wasm.HeapU8::get_Item(System.Int32)": case "System.Byte Wasm.HeapU8::get_Item(System.Int32,System.Int32)": { JSExpression actualAddress; if (arguments.Length == 2) { actualAddress = Add(typeSystem, arguments[0], arguments[1]); } else { actualAddress = arguments[0]; } return(new GetMemory(typeSystem.Byte, false, actualAddress)); } case "System.Void Wasm.HeapI32::set_Item(System.Int32,System.Int32)": case "System.Void Wasm.HeapI32::set_Item(System.Int32,System.Int32,System.Int32)": { JSExpression actualAddress; if (arguments.Length == 3) { actualAddress = Add(typeSystem, arguments[0], arguments[1]); } else { actualAddress = arguments[0]; } // HACK: Indices are in elements, not bytes var actualAddressBytes = Mul(typeSystem, actualAddress, JSLiteral.New(4)); return(new SetMemory(typeSystem.Int32, false, actualAddressBytes, arguments[arguments.Length - 1])); } case "System.Void Wasm.HeapU8::set_Item(System.Int32,System.Byte)": case "System.Void Wasm.HeapU8::set_Item(System.Int32,System.Int32,System.Byte)": { JSExpression actualAddress; if (arguments.Length == 3) { actualAddress = Add(typeSystem, arguments[0], arguments[1]); } else { actualAddress = arguments[0]; } return(new SetMemory(typeSystem.Byte, false, actualAddress, arguments[arguments.Length - 1])); } case "System.Char System.String::get_Chars(System.Int32)": { var actualAddress = Add(typeSystem, thisExpression, arguments[0]); return(new GetMemory( typeSystem.Byte, false, actualAddress )); } case "System.Int32 System.String::get_Length()": { return(new GetStringLength(thisExpression)); } case "System.Int32* Wasm.HeapI32::get_Base()": case "System.Byte* Wasm.HeapU8::get_Base()": { return(JSLiteral.DefaultValue(method.ReturnType)); } case "System.Void Wasm.Heap::SetStdout(System.String)": { // FIXME: Store the filename somewhere return(new JSNullExpression()); } case "System.Void Wasm.Heap::Write(System.Int32,System.Int32)": { return(new StdoutWrite(arguments[0], arguments[1])); } } if (false) { Console.WriteLine("// Treating method '{0}' as runtime call", fullName); } return(null); }
public void VisitNode(JSCastExpression ce) { var currentType = ce.Expression.GetActualType(TypeSystem); var targetType = ce.NewType; JSExpression newExpression = null; var innerCast = ce.Expression as JSCastExpression; if (targetType.FullName == "System.ValueType") { var replacement = ce.Expression; ParentNode.ReplaceChild(ce, replacement); VisitReplacement(replacement); return; } else if ( TypeUtil.IsIntegralOrEnum(currentType) && (targetType.MetadataType == MetadataType.Char) ) { newExpression = JSInvocationExpression.InvokeStatic( JS.fromCharCode, new[] { ce.Expression }, true ); } else if ( (currentType.MetadataType == MetadataType.Char) && TypeUtil.IsIntegral(targetType) ) { newExpression = JSInvocationExpression.InvokeMethod( JS.charCodeAt, ce.Expression, new[] { JSLiteral.New(0) }, true ); } else if ( IntroduceEnumCasts.IsEnumOrNullableEnum(currentType) ) { TypeInfo enumInfo; var isNullable = TypeUtil.IsNullable(currentType); if (isNullable) { int temp; var git = (GenericInstanceType)TypeUtil.FullyDereferenceType(currentType, out temp); enumInfo = TypeInfo.Get(git.GenericArguments[0]); } else { enumInfo = TypeInfo.Get(currentType); } if (enumInfo == null) { throw new InvalidOperationException("Unable to extract enum type from typereference " + currentType); } if (targetType.MetadataType == MetadataType.Boolean) { newExpression = new JSBinaryOperatorExpression( JSOperator.NotEqual, JSCastExpression.New(ce.Expression, TypeSystem.Int32, TypeSystem, true, true), new JSIntegerLiteral(0, typeof(Int32)), TypeSystem.Boolean ); } else if (TypeUtil.IsNumeric(targetType)) { if (isNullable) { newExpression = new JSNullableCastExpression(ce.Expression, new JSType(targetType)); } else if ( ce.Expression is JSCastExpression && (((JSCastExpression)ce.Expression).Expression.GetActualType(TypeSystem).MetadataType == MetadataType.Int64 || ((JSCastExpression)ce.Expression).Expression.GetActualType(TypeSystem).MetadataType == MetadataType.UInt64) ) { newExpression = ce.Expression; } else { newExpression = JSInvocationExpression.InvokeMethod( JS.valueOf(targetType), ce.Expression, null, true ); } } else if (targetType.FullName == "System.Enum") { newExpression = ce.Expression; } else { // Debugger.Break(); } } else if ( (targetType.MetadataType == MetadataType.Boolean) && (ce.Expression is JSAsExpression) && ((JSAsExpression)ce.Expression).GetActualType(TypeSystem) is GenericParameter ) { // C# expressions such as (t is T) (where T is a generic parameter). See issue #150. // Tested with AsWithGenericParameter.cs newExpression = new JSBinaryOperatorExpression( JSOperator.NotEqual, ce.Expression, new JSNullLiteral(currentType), TypeSystem.Boolean ); } else if ( (targetType.MetadataType == MetadataType.Boolean) && // A cast from Object to Boolean can occur in two forms: // An implied conversion, where an object expression is treated as a boolean (logicnot operation, etc). // In this case, we want to do 'obj != null' to make it a boolean. // An explicit conversion, where an object expression is unboxed to boolean. // In this case we want to leave it as-is. (ce.IsCoercion || (currentType.FullName != "System.Object")) ) { newExpression = new JSBinaryOperatorExpression( JSOperator.NotEqual, ce.Expression, new JSDefaultValueLiteral(currentType), TypeSystem.Boolean ); } else if ( TypeUtil.IsNumeric(targetType) && TypeUtil.IsNumeric(currentType) && !TypeUtil.TypesAreEqual(targetType, currentType, true) ) { TypeReference innerType = null; if (innerCast != null) { innerType = innerCast.Expression.GetActualType(TypeSystem); } if ( TypeUtil.TypesAreAssignable(TypeInfo, targetType, innerType) && ( (TypeUtil.IsFloatingPoint(targetType) == TypeUtil.IsFloatingPoint(currentType)) && (TypeUtil.IsFloatingPoint(targetType) == TypeUtil.IsFloatingPoint(innerType)) ) && (TypeUtil.IsSigned(targetType) == TypeUtil.IsSigned(innerType)) && (TypeUtil.SizeOfType(targetType) <= TypeUtil.SizeOfType(currentType)) && (TypeUtil.SizeOfType(targetType) >= TypeUtil.SizeOfType(innerType)) ) { // HACK: Turn pointless conversions like '(int32)(int64)(1 + 2)' into '(1 + 2)' newExpression = innerCast.Expression; } else if (currentType.MetadataType == MetadataType.Int64) { if (!EmulateInt64) { newExpression = ce; } else if (targetType.MetadataType == MetadataType.UInt64) { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToUInt64", TypeSystem.UInt64, new TypeReference[] { }, MethodTypeFactory), ce.Expression); } else { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToNumber", targetType, new TypeReference[] { TypeSystem.Double, TypeSystem.Boolean }, MethodTypeFactory), ce.Expression, GetInt64ConversionArgs(targetType)); } } else if (currentType.MetadataType == MetadataType.UInt64) { if (!EmulateInt64) { newExpression = ce; } else if (targetType.MetadataType == MetadataType.Int64) { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToInt64", TypeSystem.Int64, new TypeReference[] { }, MethodTypeFactory), ce.Expression); } else { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToNumber", targetType, new TypeReference[] { TypeSystem.Double, TypeSystem.Boolean }, MethodTypeFactory), ce.Expression, GetInt64ConversionArgs(targetType)); } } else if (targetType.MetadataType == MetadataType.Int64) { if (!EmulateInt64) { newExpression = ce; } else { newExpression = JSInvocationExpression.InvokeStatic( new JSType(TypeSystem.Int64), new JSFakeMethod("FromNumber", TypeSystem.Int64, new[] { currentType }, MethodTypeFactory), new[] { ce.Expression }, true ); } } else if (targetType.MetadataType == MetadataType.UInt64) { if (!EmulateInt64) { newExpression = ce; } else { newExpression = JSInvocationExpression.InvokeStatic( new JSType(TypeSystem.UInt64), new JSFakeMethod("FromNumber", TypeSystem.UInt64, new[] { currentType }, MethodTypeFactory), new[] { ce.Expression }, true ); } } else if (TypeUtil.IsIntegral(currentType)) { if (!TypeUtil.IsIntegral(targetType)) { // Integer -> float conversion newExpression = new JSIntegerToFloatExpression(ce.Expression, targetType); } else if (TypeUtil.SizeOfType(currentType) < TypeUtil.SizeOfType(targetType)) { // Widening integer -> integer conversion newExpression = ce.Expression; } else { newExpression = null; } } else if (TypeUtil.IsIntegral(targetType)) { newExpression = new JSTruncateExpression(ce.Expression); } else if (TypeUtil.SizeOfType(targetType) < TypeUtil.SizeOfType(currentType)) { // Narrowing double -> float conversion newExpression = new JSDoubleToFloatExpression(ce.Expression); } else { // Widening float -> double conversion newExpression = new JSFloatToDoubleExpression(ce.Expression); } } else { // newExpression = JSIL.Cast(ce.Expression, targetType); } if ((newExpression != null) && (newExpression != ce)) { ParentNode.ReplaceChild(ce, newExpression); VisitReplacement(newExpression); } else { // Debugger.Break(); VisitChildren(ce); } }
public void VisitNode(JSCastExpression ce) { var currentType = ce.Expression.GetActualType(TypeSystem); var targetType = ce.NewType; JSExpression newExpression = null; if (targetType.FullName == "System.ValueType") { var replacement = ce.Expression; ParentNode.ReplaceChild(ce, replacement); VisitReplacement(replacement); return; } else if (targetType.MetadataType == MetadataType.Char) { newExpression = JSInvocationExpression.InvokeStatic( JS.fromCharCode, new[] { ce.Expression }, true ); } else if ( (currentType.MetadataType == MetadataType.Char) && TypeUtil.IsIntegral(targetType) ) { newExpression = JSInvocationExpression.InvokeMethod( JS.charCodeAt, ce.Expression, new[] { JSLiteral.New(0) }, true ); } else if ( IntroduceEnumCasts.IsEnumOrNullableEnum(currentType) ) { var enumInfo = TypeInfo.Get(currentType); var isNullable = TypeUtil.IsNullable(currentType); if (targetType.MetadataType == MetadataType.Boolean) { EnumMemberInfo enumMember; if (enumInfo.ValueToEnumMember.TryGetValue(0, out enumMember)) { newExpression = new JSBinaryOperatorExpression( JSOperator.NotEqual, ce.Expression, new JSEnumLiteral(enumMember.Value, enumMember), TypeSystem.Boolean ); } else if (enumInfo.ValueToEnumMember.TryGetValue(1, out enumMember)) { newExpression = new JSBinaryOperatorExpression( JSOperator.Equal, ce.Expression, new JSEnumLiteral(enumMember.Value, enumMember), TypeSystem.Boolean ); } else { newExpression = new JSUntranslatableExpression(String.Format( "Could not cast enum of type '{0}' to boolean because it has no zero value or one value", currentType.FullName )); } } else if (TypeUtil.IsNumeric(targetType)) { if (isNullable) { newExpression = JSIL.ValueOfNullable( ce.Expression ); } else { newExpression = JSInvocationExpression.InvokeMethod( JS.valueOf(targetType), ce.Expression, null, true ); } } else if (targetType.FullName == "System.Enum") { newExpression = ce.Expression; } else { // Debugger.Break(); } } else if ( (targetType.MetadataType == MetadataType.Boolean) && // A cast from Object to Boolean can occur in two forms: // An implied conversion, where an object expression is treated as a boolean (logicnot operation, etc). // In this case, we want to do 'obj != null' to make it a boolean. // An explicit conversion, where an object expression is unboxed to boolean. // In this case we want to leave it as-is. (ce.IsCoercion || (currentType.FullName != "System.Object")) ) { newExpression = new JSBinaryOperatorExpression( JSBinaryOperator.NotEqual, ce.Expression, new JSDefaultValueLiteral(currentType), TypeSystem.Boolean ); } else if ( TypeUtil.IsNumeric(targetType) && TypeUtil.IsNumeric(currentType) ) { if ( TypeUtil.IsIntegral(currentType) || !TypeUtil.IsIntegral(targetType) ) { newExpression = ce.Expression; } else { newExpression = new JSTruncateExpression(ce.Expression); } } else { // newExpression = JSIL.Cast(ce.Expression, targetType); } if (newExpression != null) { ParentNode.ReplaceChild(ce, newExpression); VisitReplacement(newExpression); } else { // Debugger.Break(); VisitChildren(ce); } }
public void VisitNode(JSInvocationExpression ie) { var type = ie.JSType; var method = ie.JSMethod; var thisExpression = ie.ThisReference; if (method != null) { if ( (type != null) && (type.Type.FullName == "System.Object") ) { switch (method.Method.Member.Name) { case ".ctor": { var replacement = new JSNullExpression(); ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } case "ReferenceEquals": { var lhs = ie.Arguments[0]; var rhs = ie.Arguments[1]; var lhsType = lhs.GetActualType(TypeSystem); var rhsType = rhs.GetActualType(TypeSystem); JSNode replacement; // Structs can never compare equal with ReferenceEquals if (TypeUtil.IsStruct(lhsType) || TypeUtil.IsStruct(rhsType)) { replacement = JSLiteral.New(false); } else { replacement = new JSBinaryOperatorExpression( JSOperator.Equal, lhs, rhs, TypeSystem.Boolean ); } ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } case "GetType": { JSNode replacement; var thisType = JSExpression.DeReferenceType(thisExpression.GetActualType(TypeSystem), false); if ((thisType is GenericInstanceType) && thisType.FullName.StartsWith("System.Nullable")) { var git = (GenericInstanceType)thisType; replacement = new JSTernaryOperatorExpression( new JSBinaryOperatorExpression( JSOperator.NotEqual, thisExpression, new JSNullLiteral(thisType), TypeSystem.Boolean ), new JSTypeOfExpression(git.GenericArguments[0]), JSIL.ThrowNullReferenceException(), new TypeReference("System", "Type", TypeSystem.Object.Module, TypeSystem.Object.Scope, false) ); } else { replacement = JSIL.GetTypeOf(thisExpression); } ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } } } else if ( (type != null) && (type.Type.FullName == "System.ValueType") ) { switch (method.Method.Member.Name) { case "Equals": { var replacement = JSIL.StructEquals(ie.ThisReference, ie.Arguments.First()); ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } } } else if ( (type != null) && IsNullable(type.Type) ) { var t = (type.Type as GenericInstanceType).GenericArguments[0]; var @null = JSLiteral.Null(t); var @default = new JSDefaultValueLiteral(t); switch (method.Method.Member.Name) { case ".ctor": JSExpression value; if (ie.Arguments.Count == 0) { value = @null; } else { value = ie.Arguments[0]; } var boe = new JSBinaryOperatorExpression( JSOperator.Assignment, ie.ThisReference, value, type.Type ); ParentNode.ReplaceChild(ie, boe); VisitReplacement(boe); break; case "GetValueOrDefault": { var replacement = JSIL.ValueOfNullableOrDefault( ie.ThisReference, (ie.Arguments.Count == 0) ? @default : ie.Arguments[0] ); if (ParentNode is JSResultReferenceExpression) { // HACK: Replacing the invocation inside a result reference is incorrect, so we need to walk up the stack // and replace the result reference with the ternary instead. _ResultReferenceReplacement = replacement; } else { ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); } break; } case "get_HasValue": { var replacement = JSIL.NullableHasValue(ie.ThisReference); if (ParentNode is JSResultReferenceExpression) { _ResultReferenceReplacement = replacement; } else { ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); } break; } case "get_Value": { var replacement = JSIL.ValueOfNullable(ie.ThisReference); if (ParentNode is JSResultReferenceExpression) { _ResultReferenceReplacement = replacement; } else { ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); } break; } case "Equals": JSBinaryOperatorExpression equality = new JSBinaryOperatorExpression(JSOperator.Equal, ie.ThisReference, ie.Parameters.First().Value, type.Type); ParentNode.ReplaceChild(ie, equality); VisitReplacement(equality); break; default: throw new NotImplementedException(method.Method.Member.FullName); } return; } else if ( (type != null) && TypeUtil.TypesAreEqual(TypeSystem.String, type.Type) && (method.Method.Name == "Concat") ) { if (ie.Arguments.Count > 2) { if (ie.Arguments.All( (arg) => TypeUtil.TypesAreEqual( TypeSystem.String, arg.GetActualType(TypeSystem) ) )) { var boe = JSBinaryOperatorExpression.New( JSOperator.Add, ie.Arguments, TypeSystem.String ); ParentNode.ReplaceChild( ie, boe ); VisitReplacement(boe); } } else if ( // HACK: Fix for #239, only convert concat call into + if both sides are non-null literals (ie.Arguments.Count == 2) ) { var lhs = ie.Arguments[0]; var rhs = ie.Arguments[1]; var isAddOk = (lhs is JSStringLiteral) && (rhs is JSStringLiteral); var lhsType = TypeUtil.DereferenceType(lhs.GetActualType(TypeSystem)); if (!( TypeUtil.TypesAreEqual(TypeSystem.String, lhsType) || TypeUtil.TypesAreEqual(TypeSystem.Char, lhsType) )) { lhs = JSInvocationExpression.InvokeMethod(lhsType, JS.toString, lhs, null); isAddOk = true; } var rhsType = TypeUtil.DereferenceType(rhs.GetActualType(TypeSystem)); if (!( TypeUtil.TypesAreEqual(TypeSystem.String, rhsType) || TypeUtil.TypesAreEqual(TypeSystem.Char, rhsType) )) { rhs = JSInvocationExpression.InvokeMethod(rhsType, JS.toString, rhs, null); isAddOk = true; } if (isAddOk) { var boe = new JSBinaryOperatorExpression( JSOperator.Add, lhs, rhs, TypeSystem.String ); ParentNode.ReplaceChild( ie, boe ); VisitReplacement(boe); } } else if ( TypeUtil.GetTypeDefinition(ie.Arguments[0].GetActualType(TypeSystem)).FullName == "System.Array" ) { } else { var firstArg = ie.Arguments.FirstOrDefault(); ParentNode.ReplaceChild( ie, firstArg ); if (firstArg != null) { VisitReplacement(firstArg); } } return; } else if ( TypeUtil.IsDelegateType(method.Reference.DeclaringType) && (method.Method.Name == "Invoke") ) { var newIe = new JSDelegateInvocationExpression( thisExpression, ie.GetActualType(TypeSystem), ie.Arguments.ToArray() ); ParentNode.ReplaceChild(ie, newIe); VisitReplacement(newIe); return; } else if ( (method.Reference.DeclaringType.Name == "RuntimeHelpers") && (method.Method.Name == "InitializeArray") ) { var array = ie.Arguments[0]; var arrayType = array.GetActualType(TypeSystem); var field = ie.Arguments[1].SelfAndChildrenRecursive.OfType <JSField>().First(); var initializer = JSArrayExpression.UnpackArrayInitializer(arrayType, field.Field.Member.InitialValue); var copy = JSIL.ShallowCopy(array, initializer, arrayType); ParentNode.ReplaceChild(ie, copy); VisitReplacement(copy); return; } else if ( method.Reference.DeclaringType.FullName == "System.Reflection.Assembly" ) { switch (method.Reference.Name) { case "GetExecutingAssembly": { var assembly = Method.DeclaringType.Module.Assembly; var asmNode = new JSReflectionAssembly(assembly); ParentNode.ReplaceChild(ie, asmNode); VisitReplacement(asmNode); return; } } } else if ( method.Method.DeclaringType.Definition.FullName == "System.Array" && (ie.Arguments.Count == 1) ) { switch (method.Method.Name) { case "GetLength": case "GetUpperBound": { var index = ie.Arguments[0] as JSLiteral; if (index != null) { var newDot = JSDotExpression.New(thisExpression, new JSStringIdentifier( String.Format("length{0}", Convert.ToInt32(index.Literal)), TypeSystem.Int32 )); if (method.Method.Name == "GetUpperBound") { var newExpr = new JSBinaryOperatorExpression( JSOperator.Subtract, newDot, JSLiteral.New(1), TypeSystem.Int32 ); ParentNode.ReplaceChild(ie, newExpr); } else { ParentNode.ReplaceChild(ie, newDot); } } break; } case "GetLowerBound": ParentNode.ReplaceChild(ie, JSLiteral.New(0)); break; } } } VisitChildren(ie); }
void EmitCast(JSExpression value, TypeReference toType) { var fromType = value.GetActualType(TypeSystem); var fromIntegral = TypeUtil.IsIntegral(fromType); var toIntegral = TypeUtil.IsIntegral(toType); var fromSize = WasmUtil.SizeOfType(fromType); var toSize = WasmUtil.SizeOfType(toType); var fromSign = TypeUtil.IsSigned(fromType); var toSign = TypeUtil.IsSigned(toType); var signSuffix = fromSign.GetValueOrDefault(toSign.GetValueOrDefault(true)) ? "s" : "u"; if (fromIntegral && toIntegral) { if ((toSize == 4) && (fromSize == 8)) { Formatter.WriteRaw("(i32.wrap/i64 "); Visit(value); Formatter.WriteRaw(")"); return; } else if ((toSize == 8) && (fromSize == 4)) { Formatter.WriteRaw("(i64.extend_{0}/i32 ", signSuffix); Visit(value); Formatter.WriteRaw(")"); return; } else if ((toSize == fromSize) && (toSign != fromSign)) { Visit(value); return; } else if ((toSize == 2) || (toSize == 1)) { var mask = (1 << (8 * toSize)) - 1; var synthesized = new JSBinaryOperatorExpression( JSOperator.BitwiseAnd, value, JSLiteral.New(mask), toType ); Visit(synthesized); return; } else if (toType.FullName == "JSIL.Types.NativeInt") { Visit(value); return; } } else if (toIntegral) { Formatter.WriteRaw( "({0}.trunc_{1}/{2} ", WasmUtil.PickTypeKeyword(toType), signSuffix, WasmUtil.PickTypeKeyword(fromType) ); Visit(value); Formatter.WriteRaw(")"); return; } Console.Error.WriteLine("unimplemented cast {0} -> {1}", fromType, toType); Visit(value); }