Fix unsigned ops; Fix parenthesized conversion; Minor restructure

This commit is contained in:
dcodeIO
2017-12-11 18:46:11 +01:00
parent b5ffaf36cd
commit b5cc2f9924
24 changed files with 1062 additions and 79 deletions

View File

@ -1277,9 +1277,9 @@ export class Compiler extends DiagnosticEmitter {
compileAssertionExpression(expression: AssertionExpression, contextualType: Type): ExpressionRef {
const toType: Type | null = this.program.resolveType(expression.toType, this.currentFunction.contextualTypeArguments); // reports
return toType && toType != contextualType
? this.compileExpression(expression.expression, <Type>toType, ConversionKind.EXPLICIT)
: this.compileExpression(expression.expression, contextualType);
if (!toType)
return this.module.createUnreachable();
return this.compileExpression(expression.expression, toType, ConversionKind.EXPLICIT);
}
compileBinaryExpression(expression: BinaryExpression, contextualType: Type): ExpressionRef {
@ -1300,9 +1300,13 @@ export class Compiler extends DiagnosticEmitter {
? BinaryOp.LtF32
: this.currentType == Type.f64
? BinaryOp.LtF64
: this.currentType.isSignedInteger
? this.currentType.isLongInteger
? BinaryOp.LtI64
: BinaryOp.LtI32
: this.currentType.isLongInteger
? BinaryOp.LtI64
: BinaryOp.LtI32;
? BinaryOp.LtU64
: BinaryOp.LtU32;
this.currentType = Type.bool;
break;
@ -1313,9 +1317,13 @@ export class Compiler extends DiagnosticEmitter {
? BinaryOp.GtF32
: this.currentType == Type.f64
? BinaryOp.GtF64
: this.currentType.isSignedInteger
? this.currentType.isLongInteger
? BinaryOp.GtI64
: BinaryOp.GtI32
: this.currentType.isLongInteger
? BinaryOp.GtI64
: BinaryOp.GtI32;
? BinaryOp.GtU64
: BinaryOp.GtU32;
this.currentType = Type.bool;
break;
@ -1326,9 +1334,13 @@ export class Compiler extends DiagnosticEmitter {
? BinaryOp.LeF32
: this.currentType == Type.f64
? BinaryOp.LeF64
: this.currentType.isSignedInteger
? this.currentType.isLongInteger
? BinaryOp.LeI64
: BinaryOp.LeI32
: this.currentType.isLongInteger
? BinaryOp.LeI64
: BinaryOp.LeI32;
? BinaryOp.LeU64
: BinaryOp.LeU32;
this.currentType = Type.bool;
break;
@ -1339,9 +1351,13 @@ export class Compiler extends DiagnosticEmitter {
? BinaryOp.GeF32
: this.currentType == Type.f64
? BinaryOp.GeF64
: this.currentType.isSignedInteger
? this.currentType.isLongInteger
? BinaryOp.GeI64
: BinaryOp.GeI32
: this.currentType.isLongInteger
? BinaryOp.GeI64
: BinaryOp.GeI32;
? BinaryOp.GeU64
: BinaryOp.GeU32;
this.currentType = Type.bool;
break;
@ -1427,9 +1443,13 @@ export class Compiler extends DiagnosticEmitter {
? BinaryOp.DivF32
: this.currentType == Type.f64
? BinaryOp.DivF64
: this.currentType.isSignedInteger
? this.currentType.isLongInteger
? BinaryOp.DivI64
: BinaryOp.DivI32
: this.currentType.isLongInteger
? BinaryOp.DivI64
: BinaryOp.DivI32;
? BinaryOp.DivU64
: BinaryOp.DivU32;
break;
case Token.PERCENT_EQUALS:
@ -1439,15 +1459,19 @@ export class Compiler extends DiagnosticEmitter {
right = this.compileExpression(expression.right, this.currentType);
if (this.currentType.isAnyFloat)
throw new Error("not implemented"); // TODO: internal fmod, possibly simply imported from JS
op = this.currentType.isLongInteger
? BinaryOp.RemI64
: BinaryOp.RemI32;
op = this.currentType.isSignedInteger
? this.currentType.isLongInteger
? BinaryOp.RemI64
: BinaryOp.RemI32
: this.currentType.isLongInteger
? BinaryOp.RemU64
: BinaryOp.RemU32;
break;
case Token.LESSTHAN_LESSTHAN_EQUALS:
compound = Token.EQUALS;
case Token.LESSTHAN_LESSTHAN:
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT);
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType);
op = this.currentType.isLongInteger
? BinaryOp.ShlI64
@ -1457,7 +1481,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.GREATERTHAN_GREATERTHAN_EQUALS:
compound = Token.EQUALS;
case Token.GREATERTHAN_GREATERTHAN:
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT);
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType);
op = this.currentType.isSignedInteger
? this.currentType.isLongInteger
@ -1471,7 +1495,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS:
compound = Token.EQUALS;
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN:
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.u64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT);
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.u64 : contextualType, ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType);
op = this.currentType.isLongInteger
? BinaryOp.ShrU64
@ -1481,7 +1505,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.AMPERSAND_EQUALS:
compound = Token.EQUALS;
case Token.AMPERSAND:
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT);
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType);
op = this.currentType.isLongInteger
? BinaryOp.AndI64
@ -1491,7 +1515,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.BAR_EQUALS:
compound = Token.EQUALS;
case Token.BAR:
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT);
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType);
op = this.currentType.isLongInteger
? BinaryOp.OrI64
@ -1501,7 +1525,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.CARET_EQUALS:
compound = Token.EQUALS;
case Token.CARET:
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT);
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType);
op = this.currentType.isLongInteger
? BinaryOp.XorI64
@ -1509,7 +1533,7 @@ export class Compiler extends DiagnosticEmitter {
break;
case Token.AMPERSAND_AMPERSAND: // left && right
left = this.compileExpression(expression.left, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT);
left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType);
// simplify if left is free of side effects while tolerating two levels of nesting, e.g., i32.load(i32.load(i32.const))
@ -1542,7 +1566,7 @@ export class Compiler extends DiagnosticEmitter {
);
case Token.BAR_BAR: // left || right
left = this.compileExpression(expression.left, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT);
left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE);
right = this.compileExpression(expression.right, this.currentType);
// simplify if left is free of side effects while tolerating two levels of nesting
@ -1782,16 +1806,15 @@ export class Compiler extends DiagnosticEmitter {
// local
if (element.kind == ElementKind.LOCAL) {
this.currentType = (<Local>element).type;
return this.module.createGetLocal((<Local>element).index, typeToNativeType(this.currentType = (<Local>element).type));
return this.module.createGetLocal((<Local>element).index, typeToNativeType(this.currentType));
}
// global
if (element.kind == ElementKind.GLOBAL) {
const global: Global = <Global>element;
if (global.type)
this.currentType = <Type>global.type;
if (!this.compileGlobal(global)) // reports
return this.module.createUnreachable();
this.currentType = <Type>global.type;
if (global.hasConstantValue) {
if (global.type == Type.f32)
return this.module.createF32((<Global>element).constantFloatValue);
@ -1804,7 +1827,7 @@ export class Compiler extends DiagnosticEmitter {
else
throw new Error("unexpected global type");
} else
return this.module.createGetGlobal((<Global>element).internalName, typeToNativeType(this.currentType = <Type>(<Global>element).type));
return this.module.createGetGlobal((<Global>element).internalName, typeToNativeType(this.currentType));
}
// field
@ -1859,7 +1882,8 @@ export class Compiler extends DiagnosticEmitter {
}
compileParenthesizedExpression(expression: ParenthesizedExpression, contextualType: Type): ExpressionRef {
return this.compileExpression(expression.expression, contextualType);
// does not change types, just order
return this.compileExpression(expression.expression, contextualType, ConversionKind.NONE);
}
compilePropertyAccessExpression(expression: PropertyAccessExpression, contextualType: Type): ExpressionRef {

167
src/decompiler.ts Normal file
View File

@ -0,0 +1,167 @@
import {
Module,
NativeType,
ExpressionId,
UnaryOp,
BinaryOp,
HostOp,
FunctionTypeRef,
FunctionRef,
ExpressionRef,
getFunctionBody,
getExpressionId,
getExpressionType,
getUnaryOp,
getUnaryValue,
getBinaryOp,
getBinaryLeft,
getBinaryRight,
getSelectIfTrue,
getSelectIfFalse,
getSelectCondition,
getHostOp,
getHostNameOperand,
getHostOperands,
getConstValueI32,
getConstValueI64Low,
getConstValueI64High,
getConstValueF32,
getConstValueF64,
getReturnValue,
getDropValue
} from "./module";
import { I64 } from "./util";
// TODO :-)
class Decompiler {
name: string;
text: string[] = [];
private tempI64: I64 = new I64();
// Decide whether to decompile to an AST or to text directly.
// AST is a bit more useful, text a lot more efficient.
constructor(name: string = "module.ts") {
this.name = name;
}
/** Decompiles a module to an AST that can then be serialized. */
decompile(module: Module) {
throw new Error("not implemented");
}
decompileFunction(func: FunctionRef): void {
const body: ExpressionRef = getFunctionBody(func);
throw new Error("not implemented");
}
decompileExpression(expr: ExpressionRef): void {
const id: ExpressionId = getExpressionId(expr);
const type: NativeType = getExpressionType(expr);
switch (id) {
case ExpressionId.Block:
case ExpressionId.If:
case ExpressionId.Loop:
case ExpressionId.Break:
case ExpressionId.Switch:
case ExpressionId.Call:
case ExpressionId.CallImport:
case ExpressionId.CallIndirect:
case ExpressionId.GetLocal:
case ExpressionId.SetLocal:
case ExpressionId.GetGlobal:
case ExpressionId.SetGlobal:
case ExpressionId.Load:
case ExpressionId.Store:
case ExpressionId.Const:
switch (type) {
case NativeType.I32:
this.text.push(getConstValueI32(expr).toString(10));
return;
case NativeType.I64:
this.tempI64.lo = getConstValueI64Low(expr);
this.tempI64.hi = getConstValueI64High(expr);
this.text.push(this.tempI64.toString());
return;
case NativeType.F32:
this.text.push(getConstValueF32(expr).toString(10));
return;
case NativeType.F64:
this.text.push(getConstValueF64(expr).toString(10));
return;
default:
throw new Error("unexpected const type");
}
case ExpressionId.Unary:
switch (getUnaryOp(expr)) {
// TODO
}
this.decompileExpression(getUnaryValue(expr));
return;
case ExpressionId.Binary:
this.decompileExpression(getBinaryLeft(expr));
switch (getBinaryOp(expr)) {
// TODO
}
this.decompileExpression(getBinaryRight(expr));
return;
case ExpressionId.Select:
this.text.push("select<");
this.text.push(nativeTypeToType(type));
this.text.push(">(");
this.decompileExpression(getSelectIfTrue(expr));
this.text.push(", ");
this.decompileExpression(getSelectIfFalse(expr));
this.text.push(", ");
this.decompileExpression(getSelectCondition(expr));
this.text.push(");");
return;
case ExpressionId.Drop:
this.decompileExpression(getDropValue(expr));
this.text.push(";");
return;
case ExpressionId.Return:
if (type == NativeType.None) {
this.text.push("return;");
} else {
this.text.push("return ");
this.decompileExpression(getReturnValue(expr));
this.text.push(";");
}
return;
case ExpressionId.Host:
switch (getHostOp(expr)) {
case HostOp.CurrentMemory:
case HostOp.GrowMemory:
}
break;
case ExpressionId.Nop:
this.text.push(";");
return;
case ExpressionId.Unreachable:
this.text.push("unreachable()");
return;
case ExpressionId.AtomicCmpxchg:
case ExpressionId.AtomicRMW:
case ExpressionId.AtomicWait:
case ExpressionId.AtomicWake:
}
throw new Error("not implemented")
}
}
function nativeTypeToType(type: NativeType): string {
switch (type) {
case NativeType.None: return "void";
case NativeType.I32: return "i32";
case NativeType.I64: return "i64";
case NativeType.F32: return "f32";
case NativeType.F64: return "f64";
default: throw new Error("unexpected type");
}
}

3
src/glue/js.d.ts vendored
View File

@ -1,3 +0,0 @@
// Raw memory accesses to Binaryen memory
declare function store<T = u8>(ptr: usize, val: T): void;
declare function load<T = u8>(ptr: usize): T;

View File

@ -1,4 +1,4 @@
require("../../portable-assembly");
require("../../portable");
// Copy Binaryen exports to global scope
var globalScope = typeof window !== "undefined" && window || typeof global !== "undefined" && global || self;

View File

@ -859,6 +859,41 @@ export function getBinaryRight(expr: ExpressionRef): ExpressionRef {
return _BinaryenBinaryGetRight(expr);
}
export function getSelectIfTrue(expr: ExpressionRef): ExpressionRef {
return _BinaryenSelectGetIfTrue(expr);
}
export function getSelectIfFalse(expr: ExpressionRef): ExpressionRef {
return _BinaryenSelectGetIfFalse(expr);
}
export function getSelectCondition(expr: ExpressionRef): ExpressionRef {
return _BinaryenSelectGetCondition(expr);
}
export function getReturnValue(expr: ExpressionRef): ExpressionRef {
return _BinaryenReturnGetValue(expr);
}
export function getDropValue(expr: ExpressionRef): ExpressionRef {
return _BinaryenDropGetValue(expr);
}
export function getHostOp(expr: ExpressionRef): HostOp {
return _BinaryenHostGetOp(expr);
}
export function getHostNameOperand(expr: ExpressionRef): string {
return readString(_BinaryenHostGetNameOperand(expr));
}
export function getHostOperands(expr: ExpressionRef): BinaryenExpressionRef[] {
const num: Index = _BinaryenHostGetNumOperands(expr);
const arr: BinaryenExpressionRef[] = new Array(num);
for (let i: Index = 0; i < num; ++i) arr[i] = _BinaryenHostGetOperand(expr, i);
return arr;
}
export class Relooper {
module: Module;

View File

@ -306,6 +306,10 @@ export class Range {
get atStart(): Range { return new Range(this.source, this.start, this.start); }
get atEnd(): Range { return new Range(this.source, this.end, this.end); }
toString(): string {
return this.source.text.substring(this.start, this.end);
}
}
export class Tokenizer extends DiagnosticEmitter {

View File

@ -1,5 +1,5 @@
{
"extends": "../portable-assembly.json",
"extends": "../portable.json",
"compilerOptions": {
"outDir": "../out",
"sourceMap": true