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); } }
private void EmitFunctionTable() { if (MethodTable.Count == 0) { return; } Formatter.NewLine(); Formatter.WriteRaw(";; function table"); Formatter.NewLine(); Formatter.WriteRaw("(table "); Formatter.Indent(); Formatter.NewLine(); foreach (var md in MethodTable) { var name = WasmUtil.FormatMemberName(md); Formatter.ConditionalNewLine(); Formatter.WriteRaw("${0}", name); } Formatter.Unindent(); Formatter.ConditionalNewLine(); Formatter.WriteRaw(")"); Formatter.NewLine(); }
public void VisitNode(JSInvocationExpression ie) { var jsm = ie.JSMethod; if (jsm == null) { Console.WriteLine("Can't translate non-JSMethod {0}", ie); Formatter.WriteSExpr("untranslatable.call"); return; } else if (!jsm.Method.IsStatic) { Console.WriteLine("Can't translate instance call {0}", ie); Formatter.WriteSExpr("untranslatable.call"); return; } var methodDef = jsm.Reference.Resolve(); var memberName = WasmUtil.FormatMemberName(methodDef); Formatter.WriteSExpr("call", (_) => { _.WriteRaw("${0} ", memberName); EmitArgumentList(_, ie.Arguments, false); }); }
public void EmitMethodDefinition(DecompilerContext context, MethodReference methodRef, MethodDefinition method, IAstEmitter astEmitter, bool stubbed, JSRawOutputIdentifier dollar, MethodInfo methodInfo = null) { // Skip Main() and emit it at the footer if (Assembly.EntryPoint == method) { // HACK: Store this so we can use it to emit the entry point's body later EntryPointAstEmitter = astEmitter; return; } var name = WasmUtil.FormatMemberName(method); var sExport = "Wasm.Module.ExportAttribute"; methodInfo = methodInfo ?? Translator.TypeInfoProvider.GetMethod(methodRef); if (methodInfo != null && methodInfo.Metadata.HasAttribute(sExport)) { var exportName = method.Name; var ap = methodInfo.Metadata.GetAttributeParameters(sExport); if ((ap.Count == 1) && (ap[0].Value is string)) { exportName = (string)ap[0].Value; } Switch(PrecedingType.Export); Formatter.WriteRaw("(export \"{0}\" ${1})", exportName, name); Formatter.ConditionalNewLine(); } }
public void VisitNode(JSLiteral literal) { var literalType = literal.GetActualType(TypeSystem); var typeToken = WasmUtil.PickTypeKeyword(literalType); if (typeToken == null) { Console.WriteLine("AstEmitter Unhandled literal type {0}", literalType.FullName); Formatter.WriteSExpr("untranslatable.literal"); } dynamic literalValue; if (literal is JSDefaultValueLiteral) { literalValue = 0; } else { literalValue = (dynamic)literal.Literal; if (literalValue is bool) { literalValue = (literalValue ? 1 : 0); } } Formatter.WriteSExpr( "const." + typeToken, // HACK (_) => Formatter.Value(literalValue) ); }
private void VisitLiteral(JSLiteral literal, TypeReference forcedType = null) { var literalType = forcedType ?? literal.GetActualType(TypeSystem); if ((literal is JSNullLiteral) && (literalType.FullName == "System.Object")) { // HACK: ILSpy screws up the type inference... VisitStringLiteral(null); return; } var typeToken = WasmUtil.PickTypeKeyword(literalType); if (typeToken == null) { Console.WriteLine("AstEmitter Unhandled literal type {0}", literalType.FullName); Formatter.WriteSExpr("untranslatable.literal"); } if (literalType.FullName == "System.String") { if ((literal is JSDefaultValueLiteral) || (literal is JSNullLiteral)) { VisitStringLiteral(null); } else { var literalStr = (string)literal.Literal; VisitStringLiteral(literalStr); } return; } dynamic literalValue; if (literal is JSDefaultValueLiteral) { literalValue = 0; } else { literalValue = (dynamic)literal.Literal; if (literalValue is bool) { literalValue = (literalValue ? 1 : 0); } else if (literalValue is char) { literalValue = (int)(char)literalValue; } } Formatter.WriteSExpr( typeToken + ".const", // HACK (_) => Formatter.Value(literalValue) ); }
public void VisitNode(JSVariable variable) { Formatter.WriteSExpr( "get_local", (_) => _.WriteRaw("${0}", WasmUtil.EscapeIdentifier(variable.Name)) ); }
public void VisitNode(JSBinaryOperatorExpression boe) { var boeType = boe.GetActualType(TypeSystem); var typeToken = WasmUtil.PickTypeKeyword(boeType); if (typeToken == null) { Console.WriteLine("Unhandled binary operator type {0}", boeType); return; } if (boe.Operator == JSOperator.Assignment) { Assign(boe.Left, boe.Right); return; } string keyword; if (!OperatorTable.TryGetValue(boe.Operator, out keyword)) { Console.WriteLine("Unimplemented operator {0}", boe.Operator); return; } var leftType = boe.Left.GetActualType(TypeSystem); var rightType = boe.Right.GetActualType(TypeSystem); var leftSign = TypeUtil.IsSigned(leftType); // HACK: Emit the argument type since we're comparing it if (boe.Operator is JSComparisonOperator) { typeToken = WasmUtil.PickTypeKeyword(leftType); } var signSuffix = ""; if (leftSign.HasValue && TypeUtil.IsIntegral(leftType)) { signSuffix = leftSign.Value ? "s" : "u"; } var actualKeyword = string.Format( keyword + "." + typeToken, signSuffix ); Formatter.WriteSExpr( actualKeyword, (_) => EmitArgumentList(_, new[] { boe.Left, boe.Right }, true), true, false ); }
private void Assign(JSExpression target, JSExpression value) { var leftVar = target as JSVariable; var leftField = target as JSFieldAccess; var leftProp = target as JSPropertyAccess; if (leftVar != null) { Formatter.WriteSExpr( "set_local", (_) => { _.WriteRaw("${0} ", WasmUtil.EscapeIdentifier(leftVar.Name)); Visit(value); }, lineBreakAfter: true ); } else if (leftField != null) { Formatter.WriteRaw( "(call $__set_{0} ", EscapedName(leftField) ); if (!leftField.Field.Field.IsStatic) { Visit(leftField.Target); Formatter.WriteRaw(" "); } Visit(value); Formatter.WriteRaw(")"); Formatter.NewLine(); } else if (leftProp != null) { var method = leftProp.OriginalMethod; var memberName = WasmUtil.FormatMemberName(method.Reference.Resolve()); Formatter.WriteSExpr( "call", (_) => { _.WriteRaw("${0} ", memberName); Visit(value); }, lineBreakAfter: true ); } else { Console.WriteLine("Invalid assignment target {0} {1}", target.GetType().Name, target); return; } }
public void EmitField(DecompilerContext context, IAstEmitter astEmitter, FieldDefinition field, JSRawOutputIdentifier dollar, JSExpression defaultValue) { var fieldInfo = Translator.TypeInfoProvider.GetField(field); var typeKeyword = WasmUtil.PickTypeKeyword(fieldInfo.FieldType); // Unhandled type if (typeKeyword == null) { return; } GetFieldOffset(field); }
public void VisitNode(JSIntegerToFloatExpression itfe) { var type = itfe.GetActualType(TypeSystem); var typeToken = WasmUtil.PickTypeKeyword(type); var originalType = itfe.Expression.GetActualType(TypeSystem); var originalTypeToken = WasmUtil.PickTypeKeyword(originalType); Formatter.WriteSExpr( "converts." + originalTypeToken + "." + typeToken, (_) => { Visit(itfe.Expression); } ); }
public void VisitNode(JSPropertyAccess pa) { if (pa.IsWrite) { throw new Exception("Unhandled property write: " + pa); } var method = pa.OriginalMethod; var memberName = WasmUtil.FormatMemberName(method.Reference.Resolve()); Formatter.WriteSExpr("call", (_) => { _.WriteRaw("${0}", memberName); }); }
private void Assign(JSExpression target, JSExpression value) { var leftVar = target as JSVariable; var leftField = target as JSFieldAccess; var leftProp = target as JSPropertyAccess; if (leftVar != null) { Formatter.WriteSExpr( "setlocal", (_) => { _.WriteRaw("${0} ", WasmUtil.EscapeIdentifier(leftVar.Name)); Visit(value); }, lineBreakAfter: true ); } else if (leftField != null) { Formatter.WriteSExpr( // what????? "store_global", (_) => { _.WriteRaw("${0} ", EscapedName(leftField)); Visit(value); }, lineBreakAfter: true ); } else if (leftProp != null) { var method = leftProp.OriginalMethod; var memberName = WasmUtil.FormatMemberName(method.Reference.Resolve()); Formatter.WriteSExpr( "call", (_) => { _.WriteRaw("${0} ", memberName); Visit(value); }, lineBreakAfter: true ); } else { Console.WriteLine("Invalid assignment target {0}", target); return; } }
public void EmitField(DecompilerContext context, IAstEmitter astEmitter, FieldDefinition field, JSRawOutputIdentifier dollar, JSExpression defaultValue) { var fieldInfo = Translator.TypeInfoProvider.GetField(field); var typeKeyword = WasmUtil.PickTypeKeyword(fieldInfo.FieldType); // Unhandled type if (typeKeyword == null) { return; } Switch(PrecedingType.Global); Formatter.WriteRaw("(global ${0} {1})", WasmUtil.EscapeIdentifier(fieldInfo.Name), typeKeyword); Formatter.ConditionalNewLine(); }
public void VisitNode(JSIntegerToFloatExpression itfe) { var type = itfe.GetActualType(TypeSystem); var typeToken = WasmUtil.PickTypeKeyword(type); var originalType = itfe.Expression.GetActualType(TypeSystem); var originalTypeToken = WasmUtil.PickTypeKeyword(originalType); Formatter.WriteSExpr( string.Format( "{0}.convert_s/{1}", typeToken, originalTypeToken ), (_) => { Visit(itfe.Expression); } ); }
public static Tuple <bool, string> ShouldSkipMember(MemberReference member) { var fr = member as FieldReference; var mr = member as MethodReference; if (fr != null) { return(Skip(WasmUtil.PickTypeKeyword(fr.FieldType) == null, "Unsupported field type")); } if (mr != null) { return(Skip(WasmUtil.PickTypeKeyword(mr.ReturnType) == null, "Unsupported return type")); } return(Skip(false)); }
private void EmitLabelledBlock(string label, Action <JavascriptFormatter> writeBody) { if (label == null) { writeBody(Formatter); return; } Formatter.WriteSExpr( "label", (_) => { _.WriteRaw("${0} ", WasmUtil.EscapeIdentifier(label)); _.NewLine(); writeBody(_); }, lineBreakAfter: true ); }
public GetMemory( TypeReference type, bool isAligned, JSExpression addressInBytes ) : base( string.Format( "{0}.load{1}{2}", WasmUtil.PickTypeKeyword(type), WasmUtil.PickMemoryTypeSuffix(type, false), isAligned ? "" : "/1" ), addressInBytes ) { Type = type; IsAligned = isAligned; }
private void VisitFakeMethod(JSInvocationExpression ie, JSFakeMethod fakeMethod) { if (TypeUtil.IsDelegateType(fakeMethod.ReturnType) && fakeMethod.Name == "New") { var type = (JSType)ie.Arguments[0]; // FIXME: What the heck is in arg1???? var method = UnwrapDeferred <JSMethod>(ie.Arguments[2]); var methodDef = method.Reference.Resolve(); var memberName = WasmUtil.FormatMemberName(methodDef); Formatter.WriteRaw("(i32.const {0} (; {1} ;))", AssemblyEmitter.GetFunctionIndex(methodDef), methodDef.FullName); return; } Console.WriteLine("Can't translate fake method {0}", ie); Formatter.WriteSExpr("untranslatable.call"); return; }
public SetMemory( TypeReference type, bool isAligned, JSExpression addressInBytes, JSExpression value ) : base( string.Format( "{0}.store{1}{2}", WasmUtil.PickTypeKeyword(type), WasmUtil.PickMemoryTypeSuffix(type, true), isAligned ? "" : "/1" ), addressInBytes, value ) { Type = type; IsAligned = isAligned; LineBreakInside = true; LineBreakAfter = true; }
public int?GetFieldOffset(FieldDefinition fd) { var key = fd.FullName; FieldTableEntry result; if (!FieldTable.TryGetValue(key, out result)) { var size = WasmUtil.SizeOfType(fd.FieldType); // HACK HACK HACK int offset = fd.IsStatic ? (int)ReserveHeapSpace(size) : WasmUtil.OffsetOfField(fd); result = new FieldTableEntry(offset, fd); FieldTable.Add(key, result); } return(result.Offset); }
public GetMemory( TypeReference type, bool isAligned, JSExpression addressInBytes ) : base( string.Format( "load{0}{1}.{2}", TypeUtil.IsSigned(type).GetValueOrDefault() ? "s" : "u", isAligned ? "" // WTF??? : ".1", WasmUtil.PickMemoryTypeKeyword(type) ), addressInBytes ) { Type = type; IsAligned = isAligned; }
public void VisitNode(JSInvocationExpression ie) { var jsm = ie.JSMethod; if (jsm == null) { var d = ie.Method as JSDotExpression; if (d != null) { var m = d.Member as JSFakeMethod; if (m != null) { VisitFakeMethod(ie, m); return; } } Console.WriteLine("Can't translate non-JSMethod {0}", ie); Formatter.WriteSExpr("untranslatable.call"); return; } else if (!jsm.Method.IsStatic) { Console.WriteLine("Can't translate instance call {0}", ie); Formatter.WriteSExpr("untranslatable.call"); return; } var methodDef = jsm.Reference.Resolve(); var memberName = WasmUtil.FormatMemberName(methodDef); var manyArgs = ie.Arguments.Count > 2; Formatter.WriteSExpr("call", (_) => { _.WriteRaw("${0} ", memberName); EmitArgumentList(_, ie.Arguments, manyArgs); }, lineBreakInside: manyArgs); }
public SetMemory( TypeReference type, bool isAligned, JSExpression addressInBytes, JSExpression value ) : base( string.Format( "store{0}{1}.{2}", TypeUtil.IsSigned(type).GetValueOrDefault() ? "s" : "u", isAligned ? "" // WTF??? : ".1", WasmUtil.PickMemoryTypeKeyword(type) ), addressInBytes, value ) { Type = type; IsAligned = isAligned; LineBreakInside = true; LineBreakAfter = true; }
public void EmitFunctionBody(IAstEmitter astEmitter, MethodDefinition method, JSFunctionExpression function) { // Skip Main() and emit it at the footer if (Assembly.EntryPoint == method) { // HACK: Store this so we can use it to emit the entry point's body later EntryPointAstEmitter = astEmitter; return; } var name = WasmUtil.FormatMemberName(method); Switch(PrecedingType.Function, true); Formatter.WriteRaw("(func ${0}", name); Formatter.Indent(); Formatter.NewLine(); int v = 0; foreach (var kvp in function.AllVariables) { var variable = kvp.Value; var type = WasmUtil.PickTypeKeyword(variable.IdentifierType); if (type != null) { Formatter.WriteRaw( "({0} ${1} {2}) ", variable.IsParameter ? "param" : "local", WasmUtil.EscapeIdentifier(kvp.Key), type ); if (v++ >= 3) { v = 0; Formatter.NewLine(); } } } if (function.LabelGroupCount > 0) { Formatter.NewLine(); } for (var i = 0; i < function.LabelGroupCount; i++) { Formatter.WriteRaw("(local $currentLabel_{0} i32) ", i); } var returnType = WasmUtil.PickTypeKeyword(method.ReturnType); if (returnType != "void") { Formatter.NewLine(); Formatter.WriteRaw("(result {0})", returnType); } Formatter.ConditionalNewLine(); Formatter.NewLine(); astEmitter.Emit(function.Body); Formatter.ConditionalNewLine(); Formatter.Unindent(); Formatter.WriteRaw(")"); Formatter.NewLine(); }
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(); }
public void VisitNode(JSBinaryOperatorExpression boe) { var boeType = boe.GetActualType(TypeSystem); var typeToken = WasmUtil.PickTypeKeyword(boeType); if (boe.Operator == JSOperator.Assignment) { Assign(boe.Left, boe.Right); return; } string keyword; if (!OperatorTable.TryGetValue(boe.Operator, out keyword)) { Console.WriteLine("Unimplemented operator {0}", boe.Operator); return; } var leftType = boe.Left.GetActualType(TypeSystem); var rightType = boe.Right.GetActualType(TypeSystem); var leftSign = TypeUtil.IsSigned(leftType); // HACK: Emit the argument type since we're comparing it if (boe.Operator is JSComparisonOperator) { typeToken = WasmUtil.PickTypeKeyword(leftType); } var signSuffix = ""; if ( (leftSign.HasValue && TypeUtil.IsIntegral(leftType)) || // HACK (leftType.FullName == "System.Char") ) { signSuffix = leftSign.GetValueOrDefault(true) ? "_s" : "_u"; } else if ( TypeUtil.IsPointer(leftType) || TypeUtil.IsPointer(rightType) ) { signSuffix = "_u"; } var actualKeyword = string.Format( typeToken + "." + keyword, signSuffix ); // HACK: wasm i64 shift takes i64 shift amount var right = boe.Right; if ( (boe.Operator == JSOperator.ShiftLeft) || (boe.Operator == JSOperator.ShiftRight) ) { right = JSChangeTypeExpression.New(right, leftType, TypeSystem); } if (typeToken == null) { Console.WriteLine("Unhandled binary operator type {0} ({1})", boeType, boe); return; } Formatter.WriteSExpr( actualKeyword, (_) => EmitArgumentList(_, new[] { boe.Left, right }, true), true, false ); }
public void VisitNode(JSUnaryOperatorExpression uoe) { var resultType = uoe.GetActualType(TypeSystem); var typeToken = WasmUtil.PickTypeKeyword(resultType); if (typeToken == null) { Console.WriteLine("Unhandled unary operator type {0}", resultType); return; } if (uoe.Operator == JSOperator.LogicalNot) { Formatter.WriteRaw("({0}.xor ", typeToken); Visit(uoe.Expression); Formatter.WriteRaw(" ({0}.const 1))", typeToken); return; } else if (uoe.Operator == JSOperator.Negation) { Formatter.WriteRaw("({0}.sub ({0}.const 0) ", typeToken); Visit(uoe.Expression); Formatter.WriteRaw(")", typeToken); return; } string keyword; if (!OperatorTable.TryGetValue(uoe.Operator, out keyword)) { Console.WriteLine("Unimplemented operator {0}", uoe.Operator); return; } var operandType = uoe.Expression.GetActualType(TypeSystem); var sign = TypeUtil.IsSigned(operandType); var signSuffix = ""; if ( (sign.HasValue && TypeUtil.IsIntegral(operandType)) || // HACK (operandType.FullName == "System.Char") ) { signSuffix = sign.GetValueOrDefault(true) ? "_s" : "_u"; } var actualKeyword = string.Format( typeToken + "." + keyword, signSuffix ); Formatter.WriteSExpr( actualKeyword, (_) => EmitArgumentList(_, new[] { uoe.Expression }, true), true, false ); }
public void VisitNode(JSSizeOfExpression sizeofExp) { var type = sizeofExp.Type.Type; Formatter.WriteRaw("(i32.const (; sizeof {0} ;) {1})", type, WasmUtil.SizeOfType(type)); }
private string EscapedName(FieldInfo fi) { return(WasmUtil.FormatMemberName(fi.Member)); }