mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +00:00
Initial ArrayBuffer implementation; Conditional allocation within constructors; Explicit constructor return values
This commit is contained in:
parent
8cfc479cc0
commit
9cc0fcd611
2
dist/asc.js
vendored
2
dist/asc.js
vendored
File diff suppressed because one or more lines are too long
2
dist/asc.js.map
vendored
2
dist/asc.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/assemblyscript.js
vendored
2
dist/assemblyscript.js
vendored
File diff suppressed because one or more lines are too long
2
dist/assemblyscript.js.map
vendored
2
dist/assemblyscript.js.map
vendored
File diff suppressed because one or more lines are too long
@ -2462,44 +2462,46 @@ const allocateInternalName = "allocate_memory";
|
||||
/** Compiles a memory allocation for an instance of the specified class. */
|
||||
export function compileAllocate(
|
||||
compiler: Compiler,
|
||||
cls: Class,
|
||||
classInstance: Class,
|
||||
reportNode: Node
|
||||
): ExpressionRef {
|
||||
var program = compiler.program;
|
||||
assert(cls.program == program);
|
||||
assert(classInstance.program == program);
|
||||
var module = compiler.module;
|
||||
var options = compiler.options;
|
||||
|
||||
var prototype = program.elementsLookup.get(allocateInternalName);
|
||||
if (!prototype) {
|
||||
var allocatePrototype = program.elementsLookup.get(allocateInternalName);
|
||||
if (!allocatePrototype) {
|
||||
program.error(
|
||||
DiagnosticCode.Cannot_find_name_0,
|
||||
reportNode.range, allocateInternalName
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
if (prototype.kind != ElementKind.FUNCTION_PROTOTYPE) {
|
||||
if (allocatePrototype.kind != ElementKind.FUNCTION_PROTOTYPE) {
|
||||
program.error(
|
||||
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
|
||||
reportNode.range, prototype.internalName
|
||||
reportNode.range, allocatePrototype.internalName
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
|
||||
var instance = (<FunctionPrototype>prototype).resolve(); // reports
|
||||
if (!(instance && compiler.compileFunction(instance))) return module.createUnreachable();
|
||||
var allocateInstance = (<FunctionPrototype>allocatePrototype).resolve(); // reports
|
||||
if (!(allocateInstance && compiler.compileFunction(allocateInstance))) return module.createUnreachable();
|
||||
|
||||
compiler.currentType = cls.type;
|
||||
compiler.currentType = classInstance.type;
|
||||
return module.createCall(
|
||||
instance.internalName, [
|
||||
allocateInstance.internalName, [
|
||||
options.isWasm64
|
||||
? module.createI64(cls.currentMemoryOffset)
|
||||
: module.createI32(cls.currentMemoryOffset)
|
||||
? module.createI64(classInstance.currentMemoryOffset)
|
||||
: module.createI32(classInstance.currentMemoryOffset)
|
||||
],
|
||||
options.nativeSizeType
|
||||
);
|
||||
}
|
||||
|
||||
const abortInternalName = "abort";
|
||||
|
||||
/** Compiles an abort wired to the conditionally imported 'abort' function. */
|
||||
export function compileAbort(
|
||||
compiler: Compiler,
|
||||
@ -2512,7 +2514,7 @@ export function compileAbort(
|
||||
var stringType = program.typesLookup.get("string"); // might be intended
|
||||
if (!stringType) return module.createUnreachable();
|
||||
|
||||
var abortPrototype = program.elementsLookup.get("abort"); // might be intended
|
||||
var abortPrototype = program.elementsLookup.get(abortInternalName); // might be intended
|
||||
if (!abortPrototype || abortPrototype.kind != ElementKind.FUNCTION_PROTOTYPE) return module.createUnreachable();
|
||||
|
||||
var abortInstance = (<FunctionPrototype>abortPrototype).resolve(); // reports
|
||||
|
278
src/compiler.ts
278
src/compiler.ts
@ -840,7 +840,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var typeRef = this.ensureFunctionType(instance.signature);
|
||||
var module = this.module;
|
||||
if (body) {
|
||||
let returnType = instance.signature.returnType;
|
||||
let isConstructor = instance.is(CommonFlags.CONSTRUCTOR);
|
||||
let returnType: Type = instance.signature.returnType;
|
||||
|
||||
// compile body
|
||||
let previousFunction = this.currentFunction;
|
||||
@ -848,15 +849,43 @@ export class Compiler extends DiagnosticEmitter {
|
||||
let flow = instance.flow;
|
||||
let stmt: ExpressionRef;
|
||||
if (body.kind == NodeKind.EXPRESSION) { // () => expression
|
||||
assert(!instance.isAny(CommonFlags.CONSTRUCTOR | CommonFlags.GET | CommonFlags.SET));
|
||||
assert(instance.is(CommonFlags.ARROW));
|
||||
stmt = this.compileExpression((<ExpressionStatement>body).expression, returnType);
|
||||
flow.set(FlowFlags.RETURNS);
|
||||
} else {
|
||||
assert(body.kind == NodeKind.BLOCK);
|
||||
stmt = this.compileStatement(body);
|
||||
flow.finalize();
|
||||
if (isConstructor) {
|
||||
let nativeSizeType = this.options.nativeSizeType;
|
||||
assert(instance.is(CommonFlags.INSTANCE));
|
||||
|
||||
// implicitly return `this` if the constructor doesn't always return on its own
|
||||
if (!flow.is(FlowFlags.RETURNS)) {
|
||||
|
||||
// if all branches are guaranteed to allocate, skip the final conditional allocation
|
||||
if (flow.is(FlowFlags.ALLOCATES)) {
|
||||
stmt = module.createBlock(null, [
|
||||
stmt,
|
||||
module.createGetLocal(0, nativeSizeType)
|
||||
], nativeSizeType);
|
||||
|
||||
// if not all branches are guaranteed to allocate, also append a conditional allocation
|
||||
} else {
|
||||
let parent = assert(instance.memberOf);
|
||||
assert(parent.kind == ElementKind.CLASS);
|
||||
stmt = module.createBlock(null, [
|
||||
stmt,
|
||||
module.createTeeLocal(0,
|
||||
makeConditionalAllocate(this, <Class>parent, declaration.name)
|
||||
)
|
||||
], nativeSizeType);
|
||||
}
|
||||
}
|
||||
|
||||
// make sure all branches return
|
||||
let allBranchesReturn = flow.finalize();
|
||||
if (returnType != Type.void && !allBranchesReturn) {
|
||||
} else if (returnType != Type.void && !flow.is(FlowFlags.RETURNS)) {
|
||||
this.error(
|
||||
DiagnosticCode.A_function_whose_declared_type_is_not_void_must_return_a_value,
|
||||
declaration.signature.returnType.range
|
||||
@ -1272,13 +1301,15 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
var stmt = this.module.createBlock(null, this.compileStatements(statements), NativeType.None);
|
||||
var stmtReturns = flow.is(FlowFlags.RETURNS);
|
||||
var stmtThrows = flow.is(FlowFlags.THROWS);
|
||||
var stmtAllocates = flow.is(FlowFlags.ALLOCATES);
|
||||
|
||||
// Switch back to the parent flow
|
||||
flow = flow.leaveBranchOrScope();
|
||||
this.currentFunction.flow = flow;
|
||||
if (stmtReturns) {
|
||||
flow.set(FlowFlags.RETURNS);
|
||||
}
|
||||
if (stmtReturns) flow.set(FlowFlags.RETURNS);
|
||||
if (stmtThrows) flow.set(FlowFlags.THROWS);
|
||||
if (stmtAllocates) flow.set(FlowFlags.ALLOCATES);
|
||||
return stmt;
|
||||
}
|
||||
|
||||
@ -1300,7 +1331,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
flow.set(FlowFlags.POSSIBLY_BREAKS);
|
||||
flow.set(FlowFlags.BREAKS);
|
||||
return module.createBreak(breakLabel);
|
||||
}
|
||||
|
||||
@ -1324,7 +1355,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
flow.set(FlowFlags.POSSIBLY_CONTINUES);
|
||||
flow.set(FlowFlags.CONTINUES);
|
||||
return module.createBreak(continueLabel);
|
||||
}
|
||||
|
||||
@ -1407,12 +1438,18 @@ export class Compiler extends DiagnosticEmitter {
|
||||
? this.compileExpression(<Expression>statement.incrementor, Type.void)
|
||||
: module.createNop();
|
||||
var body = this.compileStatement(statement.statement);
|
||||
|
||||
var alwaysReturns = !statement.condition && flow.is(FlowFlags.RETURNS);
|
||||
var alwaysThrows = !statement.condition && flow.is(FlowFlags.THROWS);
|
||||
var alwaysAllocates = !statement.condition && flow.is(FlowFlags.ALLOCATES);
|
||||
// TODO: check other always-true conditions as well, not just omitted
|
||||
|
||||
if (alwaysReturns) flow.set(FlowFlags.RETURNS);
|
||||
if (alwaysThrows) flow.set(FlowFlags.THROWS);
|
||||
if (alwaysAllocates) flow.set(FlowFlags.ALLOCATES);
|
||||
|
||||
// Switch back to the parent flow
|
||||
flow = flow.leaveBranchOrScope();
|
||||
currentFunction.flow = flow;
|
||||
currentFunction.flow = flow.leaveBranchOrScope();
|
||||
currentFunction.leaveBreakContext();
|
||||
|
||||
var expr = module.createBlock(breakLabel, [
|
||||
@ -1426,9 +1463,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
], NativeType.None))
|
||||
], NativeType.None);
|
||||
|
||||
// If the loop is guaranteed to run and return, propagate that and append a hint
|
||||
if (alwaysReturns) {
|
||||
flow.set(FlowFlags.RETURNS);
|
||||
// If the loop is guaranteed to run and return, append a hint
|
||||
if (alwaysReturns || alwaysThrows) {
|
||||
expr = module.createBlock(null, [
|
||||
expr,
|
||||
module.createUnreachable()
|
||||
@ -1472,22 +1508,30 @@ export class Compiler extends DiagnosticEmitter {
|
||||
currentFunction.flow = flow;
|
||||
var ifTrueExpr = this.compileStatement(ifTrue);
|
||||
var ifTrueReturns = flow.is(FlowFlags.RETURNS);
|
||||
var ifTrueThrows = flow.is(FlowFlags.THROWS);
|
||||
var ifTrueAllocates = flow.is(FlowFlags.ALLOCATES);
|
||||
flow = flow.leaveBranchOrScope();
|
||||
currentFunction.flow = flow;
|
||||
|
||||
var ifFalseExpr: ExpressionRef = 0;
|
||||
var ifFalseReturns = false;
|
||||
var ifFalseThrows = false;
|
||||
var ifFalseAllocates = false;
|
||||
if (ifFalse) {
|
||||
flow = flow.enterBranchOrScope();
|
||||
currentFunction.flow = flow;
|
||||
ifFalseExpr = this.compileStatement(ifFalse);
|
||||
ifFalseReturns = flow.is(FlowFlags.RETURNS);
|
||||
ifFalseThrows = flow.is(FlowFlags.THROWS);
|
||||
ifFalseAllocates = flow.is(FlowFlags.ALLOCATES);
|
||||
flow = flow.leaveBranchOrScope();
|
||||
currentFunction.flow = flow;
|
||||
}
|
||||
if (ifTrueReturns && ifFalseReturns) { // not necessary to append a hint
|
||||
flow.set(FlowFlags.RETURNS);
|
||||
}
|
||||
|
||||
if (ifTrueReturns && ifFalseReturns) flow.set(FlowFlags.RETURNS);
|
||||
if (ifTrueThrows && ifFalseThrows) flow.set(FlowFlags.THROWS);
|
||||
if (ifTrueAllocates && ifFalseAllocates) flow.set(FlowFlags.ALLOCATES);
|
||||
|
||||
return module.createIf(condExpr, ifTrueExpr, ifFalseExpr);
|
||||
}
|
||||
|
||||
@ -1556,6 +1600,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// nest blocks in order
|
||||
var currentBlock = module.createBlock("case0|" + context, breaks, NativeType.None);
|
||||
var alwaysReturns = true;
|
||||
var alwaysThrows = true;
|
||||
var alwaysAllocates = true;
|
||||
for (let i = 0; i < numCases; ++i) {
|
||||
let case_ = cases[i];
|
||||
let statements = case_.statements;
|
||||
@ -1577,6 +1623,12 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (!(fallsThrough || flow.is(FlowFlags.RETURNS))) {
|
||||
alwaysReturns = false; // ignore fall-throughs
|
||||
}
|
||||
if (!(fallsThrough || flow.is(FlowFlags.THROWS))) {
|
||||
alwaysThrows = false;
|
||||
}
|
||||
if (!(fallsThrough || flow.is(FlowFlags.ALLOCATES))) {
|
||||
alwaysAllocates = false;
|
||||
}
|
||||
|
||||
// Switch back to the parent flow
|
||||
currentFunction.flow = flow.leaveBranchOrScope();
|
||||
@ -1586,9 +1638,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
currentFunction.leaveBreakContext();
|
||||
|
||||
// If the switch has a default and always returns, propagate that
|
||||
if (defaultIndex >= 0 && alwaysReturns) {
|
||||
currentFunction.flow.set(FlowFlags.RETURNS);
|
||||
// Binaryen understands that so we don't need a hint
|
||||
if (defaultIndex >= 0) {
|
||||
let flow = currentFunction.flow;
|
||||
if (alwaysReturns) flow.set(FlowFlags.RETURNS);
|
||||
if (alwaysThrows) flow.set(FlowFlags.THROWS);
|
||||
if (alwaysAllocates) flow.set(FlowFlags.ALLOCATES);
|
||||
}
|
||||
return currentBlock;
|
||||
}
|
||||
@ -1596,8 +1650,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
compileThrowStatement(statement: ThrowStatement): ExpressionRef {
|
||||
var flow = this.currentFunction.flow;
|
||||
|
||||
// Remember that this branch possibly throws
|
||||
flow.set(FlowFlags.POSSIBLY_THROWS);
|
||||
// Remember that this branch throws
|
||||
flow.set(FlowFlags.THROWS);
|
||||
|
||||
// FIXME: without try-catch it is safe to assume RETURNS as well for now
|
||||
flow.set(FlowFlags.RETURNS);
|
||||
@ -1791,8 +1845,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
flow.continueLabel = continueLabel;
|
||||
|
||||
var body = this.compileStatement(statement.statement);
|
||||
var alwaysReturns = false && flow.is(FlowFlags.RETURNS);
|
||||
// TODO: evaluate possible always-true conditions
|
||||
var alwaysReturns = false; // CONDITION_IS_ALWAYS_TRUE && flow.is(FlowFlags.RETURNS);
|
||||
// TODO: evaluate if condition is always true
|
||||
|
||||
// Switch back to the parent flow
|
||||
currentFunction.flow = flow.leaveBranchOrScope();
|
||||
@ -4471,6 +4525,18 @@ export class Compiler extends DiagnosticEmitter {
|
||||
let parent = assert(currentFunction.memberOf);
|
||||
assert(parent.kind == ElementKind.CLASS);
|
||||
let thisType = (<Class>parent).type;
|
||||
if (currentFunction.is(CommonFlags.CONSTRUCTOR)) {
|
||||
let nativeSizeType = this.options.nativeSizeType;
|
||||
let flow = currentFunction.flow;
|
||||
if (!flow.is(FlowFlags.ALLOCATES)) {
|
||||
flow.set(FlowFlags.ALLOCATES);
|
||||
// must be conditional because `this` could have been provided by a derived class
|
||||
this.currentType = thisType;
|
||||
return module.createTeeLocal(0,
|
||||
makeConditionalAllocate(this, <Class>parent, expression)
|
||||
);
|
||||
}
|
||||
}
|
||||
this.currentType = thisType;
|
||||
return module.createGetLocal(0, thisType.toNativeType());
|
||||
}
|
||||
@ -4908,70 +4974,43 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var options = this.options;
|
||||
var currentFunction = this.currentFunction;
|
||||
|
||||
// obtain the class being instantiated
|
||||
var resolved = this.program.resolveExpression( // reports
|
||||
expression.expression,
|
||||
currentFunction
|
||||
);
|
||||
if (resolved) {
|
||||
if (resolved.element.kind == ElementKind.CLASS_PROTOTYPE) {
|
||||
let prototype = <ClassPrototype>resolved.element;
|
||||
let instance = prototype.resolveUsingTypeArguments( // reports
|
||||
expression.typeArguments,
|
||||
null,
|
||||
expression
|
||||
);
|
||||
if (instance) {
|
||||
let thisExpr = compileBuiltinAllocate(this, instance, expression);
|
||||
let initializers = new Array<ExpressionRef>();
|
||||
|
||||
// use a temp local for 'this'
|
||||
let tempLocal = currentFunction.getTempLocal(options.usizeType);
|
||||
initializers.push(module.createSetLocal(tempLocal.index, thisExpr));
|
||||
|
||||
// apply field initializers
|
||||
if (instance.members) {
|
||||
for (let member of instance.members.values()) {
|
||||
if (member.kind == ElementKind.FIELD) {
|
||||
let field = <Field>member;
|
||||
let fieldDeclaration = field.prototype.declaration;
|
||||
if (field.is(CommonFlags.CONST)) {
|
||||
assert(false); // there are no built-in fields currently
|
||||
} else if (fieldDeclaration && fieldDeclaration.initializer) {
|
||||
initializers.push(module.createStore(field.type.byteSize,
|
||||
module.createGetLocal(tempLocal.index, options.nativeSizeType),
|
||||
this.compileExpression(fieldDeclaration.initializer, field.type),
|
||||
field.type.toNativeType(),
|
||||
field.memoryOffset
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply constructor
|
||||
let constructorInstance = instance.constructorInstance;
|
||||
if (constructorInstance) {
|
||||
initializers.push(this.compileCallDirect(constructorInstance, expression.arguments, expression,
|
||||
module.createGetLocal(tempLocal.index, options.nativeSizeType)
|
||||
));
|
||||
}
|
||||
|
||||
// return 'this'
|
||||
initializers.push(module.createGetLocal(tempLocal.index, options.nativeSizeType));
|
||||
currentFunction.freeTempLocal(tempLocal);
|
||||
thisExpr = module.createBlock(null, initializers, options.nativeSizeType);
|
||||
|
||||
this.currentType = instance.type;
|
||||
return thisExpr;
|
||||
}
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature,
|
||||
expression.expression.range
|
||||
);
|
||||
}
|
||||
if (!resolved) return module.createUnreachable();
|
||||
if (resolved.element.kind != ElementKind.CLASS_PROTOTYPE) {
|
||||
this.error(
|
||||
DiagnosticCode.Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature,
|
||||
expression.expression.range
|
||||
);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
return module.createUnreachable();
|
||||
var classPrototype = <ClassPrototype>resolved.element;
|
||||
var classInstance = classPrototype.resolveUsingTypeArguments( // reports
|
||||
expression.typeArguments,
|
||||
null,
|
||||
expression
|
||||
);
|
||||
if (!classInstance) return module.createUnreachable();
|
||||
|
||||
var expr: ExpressionRef;
|
||||
var constructorInstance = classInstance.constructorInstance;
|
||||
|
||||
// if a constructor is present, call it with a zero `this`
|
||||
if (constructorInstance) {
|
||||
expr = this.compileCallDirect(constructorInstance, expression.arguments, expression,
|
||||
options.usizeType.toNativeZero(module)
|
||||
);
|
||||
|
||||
// otherwise simply allocate a new instance and initialize its fields
|
||||
} else {
|
||||
expr = makeAllocate(this, classInstance, expression);
|
||||
}
|
||||
|
||||
this.currentType = classInstance.type;
|
||||
return expr;
|
||||
}
|
||||
|
||||
compileParenthesizedExpression(
|
||||
@ -5764,3 +5803,78 @@ export function makeIsTrueish(expr: ExpressionRef, type: Type, module: Module):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Makes an allocation expression for an instance of the specified class. */
|
||||
export function makeAllocate(compiler: Compiler, classInstance: Class, reportNode: Node): ExpressionRef {
|
||||
var module = compiler.module;
|
||||
var currentFunction = compiler.currentFunction;
|
||||
var nativeSizeType = compiler.options.nativeSizeType;
|
||||
|
||||
var tempLocal = currentFunction.getTempLocal(classInstance.type);
|
||||
|
||||
// allocate the necessary memory
|
||||
var initializers = new Array<ExpressionRef>();
|
||||
initializers.push(
|
||||
module.createSetLocal(tempLocal.index,
|
||||
compileBuiltinAllocate(compiler, classInstance, reportNode)
|
||||
)
|
||||
);
|
||||
|
||||
// apply field initializers
|
||||
if (classInstance.members) {
|
||||
for (let member of classInstance.members.values()) {
|
||||
if (member.kind == ElementKind.FIELD) {
|
||||
let field = <Field>member;
|
||||
let fieldType = field.type;
|
||||
let fieldDeclaration = field.prototype.declaration;
|
||||
assert(!field.isAny(CommonFlags.CONST));
|
||||
if (fieldDeclaration.initializer) { // use initializer
|
||||
initializers.push(module.createStore(fieldType.byteSize,
|
||||
module.createGetLocal(tempLocal.index, nativeSizeType),
|
||||
compiler.compileExpression(fieldDeclaration.initializer, fieldType), // reports
|
||||
fieldType.toNativeType(),
|
||||
field.memoryOffset
|
||||
));
|
||||
} else { // initialize with zero
|
||||
// TODO: might be unnecessary if the ctor initializes the field
|
||||
initializers.push(module.createStore(field.type.byteSize,
|
||||
module.createGetLocal(tempLocal.index, nativeSizeType),
|
||||
field.type.toNativeZero(module),
|
||||
field.type.toNativeType(),
|
||||
field.memoryOffset
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return `this`
|
||||
initializers.push(
|
||||
module.createGetLocal(tempLocal.index, nativeSizeType)
|
||||
);
|
||||
|
||||
currentFunction.freeTempLocal(tempLocal);
|
||||
compiler.currentType = classInstance.type;
|
||||
return module.createBlock(null, initializers, nativeSizeType);
|
||||
}
|
||||
|
||||
/** Makes a conditional allocation expression inside of the constructor of the specified class. */
|
||||
export function makeConditionalAllocate(compiler: Compiler, classInstance: Class, reportNode: Node): ExpressionRef {
|
||||
// requires that `this` is the first local
|
||||
var module = compiler.module;
|
||||
var nativeSizeType = compiler.options.nativeSizeType;
|
||||
compiler.currentType = classInstance.type;
|
||||
return module.createIf(
|
||||
nativeSizeType == NativeType.I64
|
||||
? module.createBinary(
|
||||
BinaryOp.NeI64,
|
||||
module.createGetLocal(0, NativeType.I64),
|
||||
module.createI64(0)
|
||||
)
|
||||
: module.createGetLocal(0, NativeType.I32),
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
module.createTeeLocal(0,
|
||||
makeAllocate(compiler, classInstance, reportNode)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -2377,8 +2377,10 @@ export class FunctionPrototype extends Element {
|
||||
}
|
||||
|
||||
var returnType: Type;
|
||||
if (this.is(CommonFlags.SET) || this.is(CommonFlags.CONSTRUCTOR)) {
|
||||
if (this.is(CommonFlags.SET)) {
|
||||
returnType = Type.void; // not annotated
|
||||
} else if (this.is(CommonFlags.CONSTRUCTOR)) {
|
||||
returnType = assert(classInstance).type; // not annotated
|
||||
} else {
|
||||
let typeNode = assert(signatureNode.returnType);
|
||||
let type = this.program.resolveType(typeNode, contextualTypeArguments, true); // reports
|
||||
@ -3157,14 +3159,28 @@ export class Interface extends Class {
|
||||
export const enum FlowFlags {
|
||||
/** No specific conditions. */
|
||||
NONE = 0,
|
||||
|
||||
/** This branch always returns. */
|
||||
RETURNS = 1 << 0,
|
||||
/** This branch possibly throws. */
|
||||
POSSIBLY_THROWS = 1 << 1,
|
||||
/** This branch possible breaks. */
|
||||
POSSIBLY_BREAKS = 1 << 2,
|
||||
/** This branch possible continues. */
|
||||
POSSIBLY_CONTINUES = 1 << 3
|
||||
/** This branch always throws. */
|
||||
THROWS = 1 << 1,
|
||||
/** This branch always breaks. */
|
||||
BREAKS = 1 << 2,
|
||||
/** This branch always continues. */
|
||||
CONTINUES = 1 << 3,
|
||||
/** This branch always allocates. Constructors only. */
|
||||
ALLOCATES = 1 << 4,
|
||||
|
||||
/** This branch conditionally returns in a child branch. */
|
||||
CONDITIONALLY_RETURNS = 1 << 5,
|
||||
/** This branch conditionally throws in a child branch. */
|
||||
CONDITIONALLY_THROWS = 1 << 6,
|
||||
/** This branch conditionally breaks in a child branch. */
|
||||
CONDITIONALLY_BREAKS = 1 << 7,
|
||||
/** This branch conditionally continues in a child branch. */
|
||||
CONDITIONALLY_CONTINUES = 1 << 8,
|
||||
/** This branch conditionally allocates in a child branch. Constructors only. */
|
||||
CONDITIONALLY_ALLOCATES = 1 << 9
|
||||
}
|
||||
|
||||
/** A control flow evaluator. */
|
||||
@ -3200,16 +3216,18 @@ export class Flow {
|
||||
is(flag: FlowFlags): bool { return (this.flags & flag) == flag; }
|
||||
/** Sets the specified flag or flags. */
|
||||
set(flag: FlowFlags): void { this.flags |= flag; }
|
||||
/** Unsets the specified flag or flags. */
|
||||
unset(flag: FlowFlags): void { this.flags &= ~flag; }
|
||||
|
||||
/** Enters a new branch or scope and returns the new flow. */
|
||||
enterBranchOrScope(): Flow {
|
||||
var branchFlow = new Flow();
|
||||
branchFlow.parent = this;
|
||||
branchFlow.flags = this.flags;
|
||||
branchFlow.currentFunction = this.currentFunction;
|
||||
branchFlow.continueLabel = this.continueLabel;
|
||||
branchFlow.breakLabel = this.breakLabel;
|
||||
return branchFlow;
|
||||
var branch = new Flow();
|
||||
branch.parent = this;
|
||||
branch.flags = this.flags;
|
||||
branch.currentFunction = this.currentFunction;
|
||||
branch.continueLabel = this.continueLabel;
|
||||
branch.breakLabel = this.breakLabel;
|
||||
return branch;
|
||||
}
|
||||
|
||||
/** Leaves the current branch or scope and returns the parent flow. */
|
||||
@ -3225,14 +3243,20 @@ export class Flow {
|
||||
}
|
||||
|
||||
// Propagate flags to parent
|
||||
if (this.is(FlowFlags.POSSIBLY_THROWS)) {
|
||||
parent.set(FlowFlags.POSSIBLY_THROWS);
|
||||
if (this.is(FlowFlags.RETURNS)) {
|
||||
parent.set(FlowFlags.CONDITIONALLY_RETURNS);
|
||||
}
|
||||
if (this.is(FlowFlags.POSSIBLY_BREAKS) && parent.breakLabel == this.breakLabel) {
|
||||
parent.set(FlowFlags.POSSIBLY_BREAKS);
|
||||
if (this.is(FlowFlags.THROWS)) {
|
||||
parent.set(FlowFlags.CONDITIONALLY_THROWS);
|
||||
}
|
||||
if (this.is(FlowFlags.POSSIBLY_CONTINUES) && parent.continueLabel == this.continueLabel) {
|
||||
parent.set(FlowFlags.POSSIBLY_CONTINUES);
|
||||
if (this.is(FlowFlags.BREAKS) && parent.breakLabel == this.breakLabel) {
|
||||
parent.set(FlowFlags.CONDITIONALLY_BREAKS);
|
||||
}
|
||||
if (this.is(FlowFlags.CONTINUES) && parent.continueLabel == this.continueLabel) {
|
||||
parent.set(FlowFlags.CONDITIONALLY_CONTINUES);
|
||||
}
|
||||
if (this.is(FlowFlags.ALLOCATES)) {
|
||||
parent.set(FlowFlags.CONDITIONALLY_ALLOCATES);
|
||||
}
|
||||
|
||||
return parent;
|
||||
@ -3265,10 +3289,9 @@ export class Flow {
|
||||
}
|
||||
|
||||
/** Finalizes this flow. Must be the topmost parent flow of the function. */
|
||||
finalize(): bool {
|
||||
finalize(): void {
|
||||
assert(this.parent == null, "must be the topmost parent flow");
|
||||
this.continueLabel = null;
|
||||
this.breakLabel = null;
|
||||
return this.is(FlowFlags.RETURNS);
|
||||
}
|
||||
}
|
||||
|
12
std/assembly.d.ts
vendored
12
std/assembly.d.ts
vendored
@ -248,7 +248,17 @@ declare function parseI64(str: string, radix?: i32): i64;
|
||||
/** Parses a string to a 64-bit float. */
|
||||
declare function parseFloat(str: string): f64;
|
||||
|
||||
// Standard library (not yet implemented)
|
||||
// Standard library
|
||||
|
||||
/** Class representing a generic, fixed-length raw binary data buffer. */
|
||||
declare class ArrayBuffer {
|
||||
/** The size, in bytes, of the array. */
|
||||
readonly byteLength: i32;
|
||||
/** Constructs a new array buffer of the given length in bytes. */
|
||||
constructor(length: i32);
|
||||
/** Returns a copy of this array buffer's bytes from begin, inclusive, up to end, exclusive. */
|
||||
slice(begin?: i32, end?: i32): ArrayBuffer;
|
||||
}
|
||||
|
||||
/** Class representing a sequence of values of type `T`. */
|
||||
declare class Array<T> {
|
||||
|
29
std/assembly/arraybuffer.ts
Normal file
29
std/assembly/arraybuffer.ts
Normal file
@ -0,0 +1,29 @@
|
||||
const HEADER_SIZE: usize = sizeof<i32>();
|
||||
|
||||
@unmanaged
|
||||
export class ArrayBuffer {
|
||||
|
||||
readonly byteLength: i32;
|
||||
|
||||
constructor(length: i32) {
|
||||
if (<u32>length > 0x7fffffff) {
|
||||
throw new RangeError("Invalid array buffer length");
|
||||
}
|
||||
var buffer = allocate_memory(HEADER_SIZE + <usize>length);
|
||||
store<i32>(buffer, length);
|
||||
return changetype<ArrayBuffer>(buffer);
|
||||
}
|
||||
|
||||
slice(begin: i32 = 0, end: i32 = 0x7fffffff): ArrayBuffer {
|
||||
var len = this.byteLength;
|
||||
if (begin < 0) begin = max(len + begin, 0);
|
||||
else begin = min(begin, len);
|
||||
if (end < 0) end = max(len + end, 0);
|
||||
else end = min(end, len);
|
||||
var newLen = max(end - begin, 0);
|
||||
var buffer = allocate_memory(HEADER_SIZE + <usize>newLen);
|
||||
store<i32>(buffer, newLen);
|
||||
move_memory(buffer + HEADER_SIZE, changetype<usize>(this) + HEADER_SIZE + begin, newLen);
|
||||
return changetype<ArrayBuffer>(buffer);
|
||||
}
|
||||
}
|
10
std/portable.d.ts
vendored
10
std/portable.d.ts
vendored
@ -200,6 +200,16 @@ declare function parseFloat(str: string): f64;
|
||||
declare const NaN: f32 | f64;
|
||||
declare const Infinity: f32 | f64;
|
||||
|
||||
/** Class representing a generic, fixed-length raw binary data buffer. */
|
||||
declare class ArrayBuffer {
|
||||
/** The size, in bytes, of the array. */
|
||||
readonly byteLength: i32;
|
||||
/** Constructs a new array buffer of the given length in bytes. */
|
||||
constructor(length: i32);
|
||||
/** Returns a copy of this array buffer's bytes from begin, inclusive, up to end, exclusive. */
|
||||
slice(begin?: i32, end?: i32): ArrayBuffer;
|
||||
}
|
||||
|
||||
declare class Array<T> {
|
||||
[key: number]: T;
|
||||
length: i32;
|
||||
|
2392
tests/compiler/std/arraybuffer.optimized.wat
Normal file
2392
tests/compiler/std/arraybuffer.optimized.wat
Normal file
File diff suppressed because it is too large
Load Diff
39
tests/compiler/std/arraybuffer.ts
Normal file
39
tests/compiler/std/arraybuffer.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import "allocator/arena";
|
||||
|
||||
var buffer = new ArrayBuffer(8);
|
||||
|
||||
assert(buffer.byteLength == 8);
|
||||
|
||||
var sliced = buffer.slice();
|
||||
|
||||
assert(sliced.byteLength == 8);
|
||||
assert(sliced != buffer);
|
||||
|
||||
sliced = buffer.slice(1);
|
||||
|
||||
assert(sliced.byteLength == 7);
|
||||
|
||||
sliced = buffer.slice(-1);
|
||||
|
||||
assert(sliced.byteLength == 1);
|
||||
|
||||
sliced = buffer.slice(1, 3);
|
||||
|
||||
assert(sliced.byteLength == 2);
|
||||
|
||||
sliced = buffer.slice(1, -1);
|
||||
|
||||
assert(sliced.byteLength == 6);
|
||||
|
||||
sliced = buffer.slice(-3, -1);
|
||||
|
||||
assert(sliced.byteLength == 2);
|
||||
|
||||
sliced = buffer.slice(-4, 42);
|
||||
|
||||
assert(sliced.byteLength == 4);
|
||||
|
||||
sliced = buffer.slice(42);
|
||||
|
||||
assert(sliced.byteLength == 0);
|
||||
assert(sliced != null);
|
2723
tests/compiler/std/arraybuffer.untouched.wat
Normal file
2723
tests/compiler/std/arraybuffer.untouched.wat
Normal file
File diff suppressed because it is too large
Load Diff
273
tests/compiler/std/constructor.optimized.wat
Normal file
273
tests/compiler/std/constructor.optimized.wat
Normal file
@ -0,0 +1,273 @@
|
||||
(module
|
||||
(type $ii (func (param i32) (result i32)))
|
||||
(type $v (func))
|
||||
(global "$(lib)/allocator/arena/startOffset" (mut i32) (i32.const 0))
|
||||
(global "$(lib)/allocator/arena/offset" (mut i32) (i32.const 0))
|
||||
(global $std/constructor/emptyCtor (mut i32) (i32.const 0))
|
||||
(global $std/constructor/emptyCtorWithFieldInit (mut i32) (i32.const 0))
|
||||
(global $std/constructor/emptyCtorWithFieldNoInit (mut i32) (i32.const 0))
|
||||
(global $std/constructor/none (mut i32) (i32.const 0))
|
||||
(global $std/constructor/justFieldInit (mut i32) (i32.const 0))
|
||||
(global $std/constructor/justFieldNoInit (mut i32) (i32.const 0))
|
||||
(global $std/constructor/ctorReturns (mut i32) (i32.const 0))
|
||||
(global $std/constructor/b (mut i32) (i32.const 1))
|
||||
(global $std/constructor/ctorConditionallyReturns (mut i32) (i32.const 0))
|
||||
(global $std/constructor/ctorAllocates (mut i32) (i32.const 0))
|
||||
(global $std/constructor/ctorConditionallyAllocates (mut i32) (i32.const 0))
|
||||
(global $HEAP_BASE i32 (i32.const 4))
|
||||
(memory $0 1)
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func "$(lib)/allocator/arena/allocate_memory" (; 0 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(if
|
||||
(i32.eqz
|
||||
(get_local $0)
|
||||
)
|
||||
(return
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.gt_u
|
||||
(tee_local $2
|
||||
(i32.and
|
||||
(i32.add
|
||||
(i32.add
|
||||
(tee_local $1
|
||||
(get_global "$(lib)/allocator/arena/offset")
|
||||
)
|
||||
(get_local $0)
|
||||
)
|
||||
(i32.const 7)
|
||||
)
|
||||
(i32.const -8)
|
||||
)
|
||||
)
|
||||
(i32.shl
|
||||
(tee_local $0
|
||||
(current_memory)
|
||||
)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.lt_s
|
||||
(grow_memory
|
||||
(select
|
||||
(get_local $0)
|
||||
(tee_local $4
|
||||
(tee_local $3
|
||||
(i32.shr_u
|
||||
(i32.and
|
||||
(i32.add
|
||||
(i32.sub
|
||||
(get_local $2)
|
||||
(get_local $1)
|
||||
)
|
||||
(i32.const 65535)
|
||||
)
|
||||
(i32.const -65536)
|
||||
)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
)
|
||||
(i32.gt_s
|
||||
(get_local $0)
|
||||
(get_local $4)
|
||||
)
|
||||
)
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
(if
|
||||
(i32.lt_s
|
||||
(grow_memory
|
||||
(get_local $3)
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
)
|
||||
(set_global "$(lib)/allocator/arena/offset"
|
||||
(get_local $2)
|
||||
)
|
||||
(get_local $1)
|
||||
)
|
||||
(func $std/constructor/EmptyCtor#constructor (; 1 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $std/constructor/EmptyCtorWithFieldInit#constructor (; 2 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(block (result i32)
|
||||
(i32.store
|
||||
(tee_local $1
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $std/constructor/EmptyCtorWithFieldNoInit#constructor (; 3 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(block (result i32)
|
||||
(i32.store
|
||||
(tee_local $1
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $std/constructor/CtorReturns#constructor (; 4 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(func $std/constructor/CtorConditionallyReturns#constructor (; 5 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(if
|
||||
(get_global $std/constructor/b)
|
||||
(return
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $std/constructor/CtorConditionallyAllocates#constructor (; 6 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(if
|
||||
(get_global $std/constructor/b)
|
||||
(if
|
||||
(i32.eqz
|
||||
(get_local $0)
|
||||
)
|
||||
(set_local $0
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $start (; 7 ;) (type $v)
|
||||
(local $0 i32)
|
||||
(set_global "$(lib)/allocator/arena/startOffset"
|
||||
(i32.and
|
||||
(i32.add
|
||||
(get_global $HEAP_BASE)
|
||||
(i32.const 7)
|
||||
)
|
||||
(i32.const -8)
|
||||
)
|
||||
)
|
||||
(set_global "$(lib)/allocator/arena/offset"
|
||||
(get_global "$(lib)/allocator/arena/startOffset")
|
||||
)
|
||||
(set_global $std/constructor/emptyCtor
|
||||
(call $std/constructor/EmptyCtor#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/emptyCtorWithFieldInit
|
||||
(call $std/constructor/EmptyCtorWithFieldInit#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/emptyCtorWithFieldNoInit
|
||||
(call $std/constructor/EmptyCtorWithFieldNoInit#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/none
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/justFieldInit
|
||||
(block (result i32)
|
||||
(i32.store
|
||||
(tee_local $0
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
(get_local $0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/justFieldNoInit
|
||||
(block (result i32)
|
||||
(i32.store
|
||||
(tee_local $0
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
(get_local $0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/ctorReturns
|
||||
(call $std/constructor/CtorReturns#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/ctorConditionallyReturns
|
||||
(call $std/constructor/CtorConditionallyReturns#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/ctorAllocates
|
||||
(call $std/constructor/EmptyCtor#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/ctorConditionallyAllocates
|
||||
(call $std/constructor/CtorConditionallyAllocates#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
86
tests/compiler/std/constructor.ts
Normal file
86
tests/compiler/std/constructor.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import "allocator/arena";
|
||||
|
||||
// trailing conditional allocate
|
||||
class EmptyCtor {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
var emptyCtor = new EmptyCtor();
|
||||
|
||||
// trailing conditional allocate with field initializer
|
||||
class EmptyCtorWithFieldInit {
|
||||
a: i32 = 1;
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
var emptyCtorWithFieldInit = new EmptyCtorWithFieldInit();
|
||||
|
||||
// trailing conditional allocate with field initialized to zero
|
||||
class EmptyCtorWithFieldNoInit {
|
||||
a: i32;
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
var emptyCtorWithFieldNoInit = new EmptyCtorWithFieldNoInit();
|
||||
|
||||
// direct allocate
|
||||
class None {
|
||||
}
|
||||
|
||||
var none = new None();
|
||||
|
||||
// direct allocate with field initializer
|
||||
class JustFieldInit {
|
||||
a: i32 = 1;
|
||||
}
|
||||
|
||||
var justFieldInit = new JustFieldInit();
|
||||
|
||||
// direct allocate with field initialized to zero
|
||||
class JustFieldNoInit {
|
||||
a: i32;
|
||||
}
|
||||
|
||||
var justFieldNoInit = new JustFieldNoInit();
|
||||
|
||||
// explicit allocation with no extra checks
|
||||
class CtorReturns {
|
||||
constructor() {
|
||||
return changetype<CtorReturns>(allocate_memory(0));
|
||||
}
|
||||
}
|
||||
|
||||
var ctorReturns = new CtorReturns();
|
||||
|
||||
var b: bool = true;
|
||||
|
||||
// explicit allocation with a trailing conditional fallback
|
||||
class CtorConditionallyReturns {
|
||||
constructor() {
|
||||
if (b) {
|
||||
return changetype<CtorConditionallyReturns>(allocate_memory(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ctorConditionallyReturns = new CtorConditionallyReturns();
|
||||
|
||||
// implicit allocation with no extra checks
|
||||
class CtorAllocates {
|
||||
constructor() {
|
||||
this;
|
||||
}
|
||||
}
|
||||
|
||||
var ctorAllocates = new CtorAllocates();
|
||||
|
||||
// implicit allocation with a trailing conditional fallback
|
||||
class CtorConditionallyAllocates {
|
||||
constructor() {
|
||||
if (b) {
|
||||
this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ctorConditionallyAllocates = new CtorConditionallyAllocates();
|
392
tests/compiler/std/constructor.untouched.wat
Normal file
392
tests/compiler/std/constructor.untouched.wat
Normal file
@ -0,0 +1,392 @@
|
||||
(module
|
||||
(type $i (func (result i32)))
|
||||
(type $ii (func (param i32) (result i32)))
|
||||
(type $v (func))
|
||||
(global "$(lib)/allocator/common/alignment/BITS" i32 (i32.const 3))
|
||||
(global "$(lib)/allocator/common/alignment/SIZE" i32 (i32.const 8))
|
||||
(global "$(lib)/allocator/common/alignment/MASK" i32 (i32.const 7))
|
||||
(global "$(lib)/allocator/arena/startOffset" (mut i32) (i32.const 0))
|
||||
(global "$(lib)/allocator/arena/offset" (mut i32) (i32.const 0))
|
||||
(global $std/constructor/emptyCtor (mut i32) (i32.const 0))
|
||||
(global $std/constructor/emptyCtorWithFieldInit (mut i32) (i32.const 0))
|
||||
(global $std/constructor/emptyCtorWithFieldNoInit (mut i32) (i32.const 0))
|
||||
(global $std/constructor/none (mut i32) (i32.const 0))
|
||||
(global $std/constructor/justFieldInit (mut i32) (i32.const 0))
|
||||
(global $std/constructor/justFieldNoInit (mut i32) (i32.const 0))
|
||||
(global $std/constructor/ctorReturns (mut i32) (i32.const 0))
|
||||
(global $std/constructor/b (mut i32) (i32.const 1))
|
||||
(global $std/constructor/ctorConditionallyReturns (mut i32) (i32.const 0))
|
||||
(global $std/constructor/ctorAllocates (mut i32) (i32.const 0))
|
||||
(global $std/constructor/ctorConditionallyAllocates (mut i32) (i32.const 0))
|
||||
(global $HEAP_BASE i32 (i32.const 4))
|
||||
(memory $0 1)
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func "$(lib)/allocator/arena/allocate_memory" (; 0 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(local $5 i32)
|
||||
(local $6 i32)
|
||||
(if
|
||||
(i32.eqz
|
||||
(get_local $0)
|
||||
)
|
||||
(return
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_local $1
|
||||
(get_global "$(lib)/allocator/arena/offset")
|
||||
)
|
||||
(set_local $2
|
||||
(i32.and
|
||||
(i32.add
|
||||
(i32.add
|
||||
(get_local $1)
|
||||
(get_local $0)
|
||||
)
|
||||
(i32.const 7)
|
||||
)
|
||||
(i32.xor
|
||||
(i32.const 7)
|
||||
(i32.const -1)
|
||||
)
|
||||
)
|
||||
)
|
||||
(set_local $3
|
||||
(current_memory)
|
||||
)
|
||||
(if
|
||||
(i32.gt_u
|
||||
(get_local $2)
|
||||
(i32.shl
|
||||
(get_local $3)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(set_local $4
|
||||
(i32.shr_u
|
||||
(i32.and
|
||||
(i32.add
|
||||
(i32.sub
|
||||
(get_local $2)
|
||||
(get_local $1)
|
||||
)
|
||||
(i32.const 65535)
|
||||
)
|
||||
(i32.xor
|
||||
(i32.const 65535)
|
||||
(i32.const -1)
|
||||
)
|
||||
)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
(set_local $5
|
||||
(select
|
||||
(tee_local $5
|
||||
(get_local $3)
|
||||
)
|
||||
(tee_local $6
|
||||
(get_local $4)
|
||||
)
|
||||
(i32.gt_s
|
||||
(get_local $5)
|
||||
(get_local $6)
|
||||
)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.lt_s
|
||||
(grow_memory
|
||||
(get_local $5)
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
(if
|
||||
(i32.lt_s
|
||||
(grow_memory
|
||||
(get_local $4)
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(set_global "$(lib)/allocator/arena/offset"
|
||||
(get_local $2)
|
||||
)
|
||||
(return
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
(func $std/constructor/EmptyCtor#constructor (; 1 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(block
|
||||
)
|
||||
(tee_local $0
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(tee_local $0
|
||||
(block (result i32)
|
||||
(set_local $1
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $std/constructor/EmptyCtorWithFieldInit#constructor (; 2 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(block
|
||||
)
|
||||
(tee_local $0
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(tee_local $0
|
||||
(block (result i32)
|
||||
(set_local $1
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
(i32.store
|
||||
(get_local $1)
|
||||
(i32.const 1)
|
||||
)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $std/constructor/EmptyCtorWithFieldNoInit#constructor (; 3 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(block
|
||||
)
|
||||
(tee_local $0
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(tee_local $0
|
||||
(block (result i32)
|
||||
(set_local $1
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
(i32.store
|
||||
(get_local $1)
|
||||
(i32.const 0)
|
||||
)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $std/constructor/CtorReturns#constructor (; 4 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(return
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $std/constructor/CtorConditionallyReturns#constructor (; 5 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(block
|
||||
(if
|
||||
(get_global $std/constructor/b)
|
||||
(return
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(tee_local $0
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(tee_local $0
|
||||
(block (result i32)
|
||||
(set_local $1
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $std/constructor/CtorAllocates#constructor (; 6 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(block
|
||||
(drop
|
||||
(tee_local $0
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(tee_local $0
|
||||
(block (result i32)
|
||||
(set_local $1
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(get_local $0)
|
||||
)
|
||||
(func $std/constructor/CtorConditionallyAllocates#constructor (; 7 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(block
|
||||
(if
|
||||
(get_global $std/constructor/b)
|
||||
(drop
|
||||
(tee_local $0
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(tee_local $0
|
||||
(block (result i32)
|
||||
(set_local $1
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(tee_local $0
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(tee_local $0
|
||||
(block (result i32)
|
||||
(set_local $1
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $start (; 8 ;) (type $v)
|
||||
(local $0 i32)
|
||||
(set_global "$(lib)/allocator/arena/startOffset"
|
||||
(i32.and
|
||||
(i32.add
|
||||
(get_global $HEAP_BASE)
|
||||
(i32.const 7)
|
||||
)
|
||||
(i32.xor
|
||||
(i32.const 7)
|
||||
(i32.const -1)
|
||||
)
|
||||
)
|
||||
)
|
||||
(set_global "$(lib)/allocator/arena/offset"
|
||||
(get_global "$(lib)/allocator/arena/startOffset")
|
||||
)
|
||||
(set_global $std/constructor/emptyCtor
|
||||
(call $std/constructor/EmptyCtor#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/emptyCtorWithFieldInit
|
||||
(call $std/constructor/EmptyCtorWithFieldInit#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/emptyCtorWithFieldNoInit
|
||||
(call $std/constructor/EmptyCtorWithFieldNoInit#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/none
|
||||
(block (result i32)
|
||||
(set_local $0
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(get_local $0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/justFieldInit
|
||||
(block (result i32)
|
||||
(set_local $0
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
(i32.store
|
||||
(get_local $0)
|
||||
(i32.const 1)
|
||||
)
|
||||
(get_local $0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/justFieldNoInit
|
||||
(block (result i32)
|
||||
(set_local $0
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
(i32.store
|
||||
(get_local $0)
|
||||
(i32.const 0)
|
||||
)
|
||||
(get_local $0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/ctorReturns
|
||||
(call $std/constructor/CtorReturns#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/ctorConditionallyReturns
|
||||
(call $std/constructor/CtorConditionallyReturns#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/ctorAllocates
|
||||
(call $std/constructor/CtorAllocates#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(set_global $std/constructor/ctorConditionallyAllocates
|
||||
(call $std/constructor/CtorConditionallyAllocates#constructor
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -1,6 +1,6 @@
|
||||
(module
|
||||
(type $ifi (func (param i32 f32) (result i32)))
|
||||
(type $ii (func (param i32) (result i32)))
|
||||
(type $ifv (func (param i32 f32)))
|
||||
(type $v (func))
|
||||
(global "$(lib)/allocator/arena/startOffset" (mut i32) (i32.const 0))
|
||||
(global "$(lib)/allocator/arena/offset" (mut i32) (i32.const 0))
|
||||
@ -91,12 +91,33 @@
|
||||
)
|
||||
(get_local $1)
|
||||
)
|
||||
(func $std/new/AClass#constructor (; 1 ;) (type $ifv) (param $0 i32) (param $1 f32)
|
||||
(func $std/new/AClass#constructor (; 1 ;) (type $ifi) (param $0 i32) (param $1 f32) (result i32)
|
||||
(local $2 i32)
|
||||
(i32.store
|
||||
(get_local $0)
|
||||
(i32.add
|
||||
(i32.load
|
||||
(get_local $0)
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(block (result i32)
|
||||
(i32.store
|
||||
(tee_local $2
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 8)
|
||||
)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
(f32.store offset=4
|
||||
(get_local $2)
|
||||
(f32.const 2)
|
||||
)
|
||||
(tee_local $0
|
||||
(get_local $2)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
@ -105,9 +126,9 @@
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
(get_local $0)
|
||||
)
|
||||
(func $start (; 2 ;) (type $v)
|
||||
(local $0 i32)
|
||||
(set_global "$(lib)/allocator/arena/startOffset"
|
||||
(i32.and
|
||||
(i32.add
|
||||
@ -121,24 +142,9 @@
|
||||
(get_global "$(lib)/allocator/arena/startOffset")
|
||||
)
|
||||
(set_global $std/new/aClass
|
||||
(block (result i32)
|
||||
(i32.store
|
||||
(tee_local $0
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 8)
|
||||
)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
(f32.store offset=4
|
||||
(get_local $0)
|
||||
(f32.const 2)
|
||||
)
|
||||
(call $std/new/AClass#constructor
|
||||
(get_local $0)
|
||||
(f32.const 3)
|
||||
)
|
||||
(get_local $0)
|
||||
(call $std/new/AClass#constructor
|
||||
(i32.const 0)
|
||||
(f32.const 3)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
(module
|
||||
(type $i (func (result i32)))
|
||||
(type $ifi (func (param i32 f32) (result i32)))
|
||||
(type $ii (func (param i32) (result i32)))
|
||||
(type $ifv (func (param i32 f32)))
|
||||
(type $v (func))
|
||||
(global "$(lib)/allocator/common/alignment/BITS" i32 (i32.const 3))
|
||||
(global "$(lib)/allocator/common/alignment/SIZE" i32 (i32.const 8))
|
||||
@ -116,23 +116,49 @@
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
(func $std/new/AClass#constructor (; 1 ;) (type $ifv) (param $0 i32) (param $1 f32)
|
||||
(i32.store
|
||||
(get_local $0)
|
||||
(i32.add
|
||||
(i32.load
|
||||
(get_local $0)
|
||||
(func $std/new/AClass#constructor (; 1 ;) (type $ifi) (param $0 i32) (param $1 f32) (result i32)
|
||||
(local $2 i32)
|
||||
(block
|
||||
(i32.store
|
||||
(get_local $0)
|
||||
(i32.add
|
||||
(i32.load
|
||||
(tee_local $0
|
||||
(if (result i32)
|
||||
(get_local $0)
|
||||
(get_local $0)
|
||||
(tee_local $0
|
||||
(block (result i32)
|
||||
(set_local $2
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 8)
|
||||
)
|
||||
)
|
||||
(i32.store
|
||||
(get_local $2)
|
||||
(i32.const 1)
|
||||
)
|
||||
(f32.store offset=4
|
||||
(get_local $2)
|
||||
(f32.const 2)
|
||||
)
|
||||
(get_local $2)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
(f32.store offset=4
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
(f32.store offset=4
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
(get_local $0)
|
||||
)
|
||||
(func $start (; 2 ;) (type $v)
|
||||
(local $0 i32)
|
||||
(set_global "$(lib)/allocator/arena/startOffset"
|
||||
(i32.and
|
||||
(i32.add
|
||||
@ -149,25 +175,9 @@
|
||||
(get_global "$(lib)/allocator/arena/startOffset")
|
||||
)
|
||||
(set_global $std/new/aClass
|
||||
(block (result i32)
|
||||
(set_local $0
|
||||
(call "$(lib)/allocator/arena/allocate_memory"
|
||||
(i32.const 8)
|
||||
)
|
||||
)
|
||||
(i32.store
|
||||
(get_local $0)
|
||||
(i32.const 1)
|
||||
)
|
||||
(f32.store offset=4
|
||||
(get_local $0)
|
||||
(f32.const 2)
|
||||
)
|
||||
(call $std/new/AClass#constructor
|
||||
(get_local $0)
|
||||
(f32.const 3)
|
||||
)
|
||||
(get_local $0)
|
||||
(call $std/new/AClass#constructor
|
||||
(i32.const 0)
|
||||
(f32.const 3)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user