mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-21 10:41:42 +00:00
Add a 'call_indirect' builtin to emit arbitrary calls (might trap at runtime); Optimize 'for' loop compilation a bit
This commit is contained in:
@ -2203,6 +2203,65 @@ export function compileCall(
|
||||
flow.unset(FlowFlags.UNCHECKED_CONTEXT);
|
||||
return ret;
|
||||
}
|
||||
case "call_indirect": { // call_indirect<T?>(target: Function | u32, ...args: *[]) -> T
|
||||
if (operands.length < 1) {
|
||||
if (typeArguments) {
|
||||
if (typeArguments.length) compiler.currentType = typeArguments[0];
|
||||
if (typeArguments.length != 1) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_type_arguments_but_got_1,
|
||||
reportNode.range, "1", typeArguments.length.toString(10)
|
||||
);
|
||||
}
|
||||
}
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_at_least_0_arguments_but_got_1,
|
||||
reportNode.range, "1", operands.length.toString(10)
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
let returnType: Type;
|
||||
if (typeArguments) {
|
||||
if (typeArguments.length != 1) {
|
||||
if (typeArguments.length) compiler.currentType = typeArguments[0];
|
||||
compiler.error(
|
||||
DiagnosticCode.Expected_0_type_arguments_but_got_1,
|
||||
reportNode.range, "1", typeArguments.length.toString(10)
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
returnType = typeArguments[0];
|
||||
} else {
|
||||
returnType = contextualType;
|
||||
}
|
||||
arg0 = compiler.compileExpressionRetainType(operands[0], Type.u32, WrapMode.NONE);
|
||||
if (compiler.currentType.kind != TypeKind.U32) {
|
||||
compiler.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
operands[0].range
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
let numOperands = operands.length - 1;
|
||||
let operandExprs = new Array<ExpressionRef>(numOperands);
|
||||
let signatureParts = new Array<string>(numOperands + 1);
|
||||
let nativeReturnType = returnType.toNativeType();
|
||||
let nativeParamTypes = new Array<NativeType>(numOperands);
|
||||
for (let i = 0; i < numOperands; ++i) {
|
||||
operandExprs[i] = compiler.compileExpressionRetainType(operands[1 + i], Type.i32, WrapMode.NONE);
|
||||
let operandType = compiler.currentType;
|
||||
signatureParts[i] = operandType.toSignatureString();
|
||||
nativeParamTypes[i] = operandType.toNativeType();
|
||||
}
|
||||
signatureParts[numOperands] = returnType.toSignatureString();
|
||||
let typeName = signatureParts.join("");
|
||||
let typeRef = module.getFunctionTypeBySignature(nativeReturnType, nativeParamTypes);
|
||||
if (!typeRef) typeRef = module.addFunctionType(typeName, nativeReturnType, nativeParamTypes);
|
||||
compiler.currentType = returnType;
|
||||
// of course this can easily result in a 'RuntimeError: function signature mismatch' trap and
|
||||
// thus must be used with care. it exists because it *might* be useful in specific scenarios.
|
||||
return module.createCallIndirect(arg0, operandExprs, typeName);
|
||||
}
|
||||
|
||||
// conversions
|
||||
|
||||
|
@ -859,10 +859,10 @@ export class Compiler extends DiagnosticEmitter {
|
||||
/** Compiles a readily resolved function instance. */
|
||||
compileFunction(instance: Function): bool {
|
||||
if (instance.is(CommonFlags.COMPILED)) return true;
|
||||
assert(!instance.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN) || instance.internalName == "abort");
|
||||
assert(!instance.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN));
|
||||
instance.set(CommonFlags.COMPILED);
|
||||
|
||||
// check that modifiers are matching but still compile as-is
|
||||
// check that modifiers are matching
|
||||
var declaration = instance.prototype.declaration;
|
||||
var body = declaration.body;
|
||||
if (body) {
|
||||
@ -1601,13 +1601,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
flow.breakLabel = breakLabel;
|
||||
var continueLabel = "continue|" + label;
|
||||
flow.continueLabel = continueLabel;
|
||||
var loopLabel = "loop|" + label;
|
||||
var repeatLabel = "repeat|" + label;
|
||||
|
||||
// Compile in correct order
|
||||
var module = this.module;
|
||||
var initExpr = statement.initializer
|
||||
? this.compileStatement(<Statement>statement.initializer)
|
||||
: module.createNop();
|
||||
: 0;
|
||||
var condExpr: ExpressionRef = 0;
|
||||
var alwaysTrue = true;
|
||||
if (statement.condition) {
|
||||
@ -1635,7 +1635,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
var incrExpr = statement.incrementor
|
||||
? this.compileExpression(<Expression>statement.incrementor, Type.void, ConversionKind.IMPLICIT, WrapMode.NONE)
|
||||
: module.createNop();
|
||||
: 0;
|
||||
var bodyExpr = this.compileStatement(statement.statement);
|
||||
|
||||
// Switch back to the parent flow
|
||||
@ -1644,19 +1644,35 @@ export class Compiler extends DiagnosticEmitter {
|
||||
currentFunction.flow = parentFlow;
|
||||
currentFunction.leaveBreakContext();
|
||||
|
||||
var expr = module.createBlock(breakLabel, [
|
||||
initExpr,
|
||||
module.createLoop(loopLabel,
|
||||
module.createBlock(null, [
|
||||
module.createBlock(continueLabel, [
|
||||
module.createBreak(breakLabel, module.createUnary(UnaryOp.EqzI32, condExpr)),
|
||||
bodyExpr
|
||||
], NativeType.None),
|
||||
incrExpr,
|
||||
module.createBreak(loopLabel)
|
||||
var breakBlock = new Array<ExpressionRef>(); // outer 'break' block
|
||||
if (initExpr) breakBlock.push(initExpr);
|
||||
|
||||
var repeatBlock = new Array<ExpressionRef>(); // block repeating the loop
|
||||
if (parentFlow.isAny(FlowFlags.CONTINUES | FlowFlags.CONDITIONALLY_CONTINUES)) {
|
||||
repeatBlock.push(
|
||||
module.createBlock(continueLabel, [ // inner 'continue' block
|
||||
module.createBreak(breakLabel, module.createUnary(UnaryOp.EqzI32, condExpr)),
|
||||
bodyExpr
|
||||
], NativeType.None)
|
||||
);
|
||||
} else { // can omit the 'continue' block
|
||||
repeatBlock.push(
|
||||
module.createBreak(breakLabel, module.createUnary(UnaryOp.EqzI32, condExpr))
|
||||
);
|
||||
repeatBlock.push(bodyExpr);
|
||||
}
|
||||
if (incrExpr) repeatBlock.push(incrExpr);
|
||||
repeatBlock.push(
|
||||
module.createBreak(repeatLabel)
|
||||
);
|
||||
|
||||
breakBlock.push(
|
||||
module.createLoop(repeatLabel,
|
||||
module.createBlock(null, repeatBlock, NativeType.None)
|
||||
)
|
||||
], NativeType.None);
|
||||
);
|
||||
|
||||
var expr = module.createBlock(breakLabel, breakBlock, NativeType.None);
|
||||
|
||||
// If the loop is guaranteed to run and return, append a hint for Binaryen
|
||||
if (flow.isAny(FlowFlags.RETURNS | FlowFlags.THROWS)) {
|
||||
|
@ -364,13 +364,6 @@ export class Parser extends DiagnosticEmitter {
|
||||
var token = tn.next();
|
||||
var startPos = tn.tokenPos;
|
||||
|
||||
// 'void'
|
||||
if (token == Token.VOID) {
|
||||
return Node.createType(
|
||||
Node.createIdentifierExpression("void", tn.range()), [], false, tn.range(startPos, tn.pos)
|
||||
);
|
||||
}
|
||||
|
||||
var type: CommonTypeNode;
|
||||
|
||||
// '(' ...
|
||||
@ -437,6 +430,12 @@ export class Parser extends DiagnosticEmitter {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 'void'
|
||||
} else if (token == Token.VOID) {
|
||||
type = Node.createType(
|
||||
Node.createIdentifierExpression("void", tn.range()), [], false, tn.range(startPos, tn.pos)
|
||||
);
|
||||
|
||||
// 'this'
|
||||
} else if (token == Token.THIS) {
|
||||
type = Node.createType(
|
||||
|
@ -2318,7 +2318,9 @@ export enum CommonFlags {
|
||||
/** Is scoped. */
|
||||
SCOPED = 1 << 24,
|
||||
/** Is a trampoline. */
|
||||
TRAMPOLINE = 1 << 25
|
||||
TRAMPOLINE = 1 << 25,
|
||||
/** Is a virtual method. */
|
||||
VIRTUAL = 1 << 26
|
||||
}
|
||||
|
||||
export enum DecoratorFlags {
|
||||
|
Reference in New Issue
Block a user