Initial ArrayBuffer implementation; Conditional allocation within constructors; Explicit constructor return values

This commit is contained in:
dcodeIO 2018-03-23 01:47:01 +01:00
parent 8cfc479cc0
commit 9cc0fcd611
18 changed files with 6285 additions and 176 deletions

2
dist/asc.js vendored

File diff suppressed because one or more lines are too long

2
dist/asc.js.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2462,44 +2462,46 @@ const allocateInternalName = "allocate_memory";
/** Compiles a memory allocation for an instance of the specified class. */ /** Compiles a memory allocation for an instance of the specified class. */
export function compileAllocate( export function compileAllocate(
compiler: Compiler, compiler: Compiler,
cls: Class, classInstance: Class,
reportNode: Node reportNode: Node
): ExpressionRef { ): ExpressionRef {
var program = compiler.program; var program = compiler.program;
assert(cls.program == program); assert(classInstance.program == program);
var module = compiler.module; var module = compiler.module;
var options = compiler.options; var options = compiler.options;
var prototype = program.elementsLookup.get(allocateInternalName); var allocatePrototype = program.elementsLookup.get(allocateInternalName);
if (!prototype) { if (!allocatePrototype) {
program.error( program.error(
DiagnosticCode.Cannot_find_name_0, DiagnosticCode.Cannot_find_name_0,
reportNode.range, allocateInternalName reportNode.range, allocateInternalName
); );
return module.createUnreachable(); return module.createUnreachable();
} }
if (prototype.kind != ElementKind.FUNCTION_PROTOTYPE) { if (allocatePrototype.kind != ElementKind.FUNCTION_PROTOTYPE) {
program.error( program.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, 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(); return module.createUnreachable();
} }
var instance = (<FunctionPrototype>prototype).resolve(); // reports var allocateInstance = (<FunctionPrototype>allocatePrototype).resolve(); // reports
if (!(instance && compiler.compileFunction(instance))) return module.createUnreachable(); if (!(allocateInstance && compiler.compileFunction(allocateInstance))) return module.createUnreachable();
compiler.currentType = cls.type; compiler.currentType = classInstance.type;
return module.createCall( return module.createCall(
instance.internalName, [ allocateInstance.internalName, [
options.isWasm64 options.isWasm64
? module.createI64(cls.currentMemoryOffset) ? module.createI64(classInstance.currentMemoryOffset)
: module.createI32(cls.currentMemoryOffset) : module.createI32(classInstance.currentMemoryOffset)
], ],
options.nativeSizeType options.nativeSizeType
); );
} }
const abortInternalName = "abort";
/** Compiles an abort wired to the conditionally imported 'abort' function. */ /** Compiles an abort wired to the conditionally imported 'abort' function. */
export function compileAbort( export function compileAbort(
compiler: Compiler, compiler: Compiler,
@ -2512,7 +2514,7 @@ export function compileAbort(
var stringType = program.typesLookup.get("string"); // might be intended var stringType = program.typesLookup.get("string"); // might be intended
if (!stringType) return module.createUnreachable(); 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(); if (!abortPrototype || abortPrototype.kind != ElementKind.FUNCTION_PROTOTYPE) return module.createUnreachable();
var abortInstance = (<FunctionPrototype>abortPrototype).resolve(); // reports var abortInstance = (<FunctionPrototype>abortPrototype).resolve(); // reports

View File

@ -840,7 +840,8 @@ export class Compiler extends DiagnosticEmitter {
var typeRef = this.ensureFunctionType(instance.signature); var typeRef = this.ensureFunctionType(instance.signature);
var module = this.module; var module = this.module;
if (body) { if (body) {
let returnType = instance.signature.returnType; let isConstructor = instance.is(CommonFlags.CONSTRUCTOR);
let returnType: Type = instance.signature.returnType;
// compile body // compile body
let previousFunction = this.currentFunction; let previousFunction = this.currentFunction;
@ -848,15 +849,43 @@ export class Compiler extends DiagnosticEmitter {
let flow = instance.flow; let flow = instance.flow;
let stmt: ExpressionRef; let stmt: ExpressionRef;
if (body.kind == NodeKind.EXPRESSION) { // () => expression if (body.kind == NodeKind.EXPRESSION) { // () => expression
assert(!instance.isAny(CommonFlags.CONSTRUCTOR | CommonFlags.GET | CommonFlags.SET));
assert(instance.is(CommonFlags.ARROW)); assert(instance.is(CommonFlags.ARROW));
stmt = this.compileExpression((<ExpressionStatement>body).expression, returnType); stmt = this.compileExpression((<ExpressionStatement>body).expression, returnType);
flow.set(FlowFlags.RETURNS); flow.set(FlowFlags.RETURNS);
} else { } else {
assert(body.kind == NodeKind.BLOCK); assert(body.kind == NodeKind.BLOCK);
stmt = this.compileStatement(body); 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 // make sure all branches return
let allBranchesReturn = flow.finalize(); } else if (returnType != Type.void && !flow.is(FlowFlags.RETURNS)) {
if (returnType != Type.void && !allBranchesReturn) {
this.error( this.error(
DiagnosticCode.A_function_whose_declared_type_is_not_void_must_return_a_value, DiagnosticCode.A_function_whose_declared_type_is_not_void_must_return_a_value,
declaration.signature.returnType.range 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 stmt = this.module.createBlock(null, this.compileStatements(statements), NativeType.None);
var stmtReturns = flow.is(FlowFlags.RETURNS); var stmtReturns = flow.is(FlowFlags.RETURNS);
var stmtThrows = flow.is(FlowFlags.THROWS);
var stmtAllocates = flow.is(FlowFlags.ALLOCATES);
// Switch back to the parent flow // Switch back to the parent flow
flow = flow.leaveBranchOrScope(); flow = flow.leaveBranchOrScope();
this.currentFunction.flow = flow; this.currentFunction.flow = flow;
if (stmtReturns) { if (stmtReturns) flow.set(FlowFlags.RETURNS);
flow.set(FlowFlags.RETURNS); if (stmtThrows) flow.set(FlowFlags.THROWS);
} if (stmtAllocates) flow.set(FlowFlags.ALLOCATES);
return stmt; return stmt;
} }
@ -1300,7 +1331,7 @@ export class Compiler extends DiagnosticEmitter {
); );
return module.createUnreachable(); return module.createUnreachable();
} }
flow.set(FlowFlags.POSSIBLY_BREAKS); flow.set(FlowFlags.BREAKS);
return module.createBreak(breakLabel); return module.createBreak(breakLabel);
} }
@ -1324,7 +1355,7 @@ export class Compiler extends DiagnosticEmitter {
); );
return module.createUnreachable(); return module.createUnreachable();
} }
flow.set(FlowFlags.POSSIBLY_CONTINUES); flow.set(FlowFlags.CONTINUES);
return module.createBreak(continueLabel); return module.createBreak(continueLabel);
} }
@ -1407,12 +1438,18 @@ export class Compiler extends DiagnosticEmitter {
? this.compileExpression(<Expression>statement.incrementor, Type.void) ? this.compileExpression(<Expression>statement.incrementor, Type.void)
: module.createNop(); : module.createNop();
var body = this.compileStatement(statement.statement); var body = this.compileStatement(statement.statement);
var alwaysReturns = !statement.condition && flow.is(FlowFlags.RETURNS); 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 // 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 // Switch back to the parent flow
flow = flow.leaveBranchOrScope(); currentFunction.flow = flow.leaveBranchOrScope();
currentFunction.flow = flow;
currentFunction.leaveBreakContext(); currentFunction.leaveBreakContext();
var expr = module.createBlock(breakLabel, [ var expr = module.createBlock(breakLabel, [
@ -1426,9 +1463,8 @@ export class Compiler extends DiagnosticEmitter {
], NativeType.None)) ], NativeType.None))
], NativeType.None); ], NativeType.None);
// If the loop is guaranteed to run and return, propagate that and append a hint // If the loop is guaranteed to run and return, append a hint
if (alwaysReturns) { if (alwaysReturns || alwaysThrows) {
flow.set(FlowFlags.RETURNS);
expr = module.createBlock(null, [ expr = module.createBlock(null, [
expr, expr,
module.createUnreachable() module.createUnreachable()
@ -1472,22 +1508,30 @@ export class Compiler extends DiagnosticEmitter {
currentFunction.flow = flow; currentFunction.flow = flow;
var ifTrueExpr = this.compileStatement(ifTrue); var ifTrueExpr = this.compileStatement(ifTrue);
var ifTrueReturns = flow.is(FlowFlags.RETURNS); var ifTrueReturns = flow.is(FlowFlags.RETURNS);
var ifTrueThrows = flow.is(FlowFlags.THROWS);
var ifTrueAllocates = flow.is(FlowFlags.ALLOCATES);
flow = flow.leaveBranchOrScope(); flow = flow.leaveBranchOrScope();
currentFunction.flow = flow; currentFunction.flow = flow;
var ifFalseExpr: ExpressionRef = 0; var ifFalseExpr: ExpressionRef = 0;
var ifFalseReturns = false; var ifFalseReturns = false;
var ifFalseThrows = false;
var ifFalseAllocates = false;
if (ifFalse) { if (ifFalse) {
flow = flow.enterBranchOrScope(); flow = flow.enterBranchOrScope();
currentFunction.flow = flow; currentFunction.flow = flow;
ifFalseExpr = this.compileStatement(ifFalse); ifFalseExpr = this.compileStatement(ifFalse);
ifFalseReturns = flow.is(FlowFlags.RETURNS); ifFalseReturns = flow.is(FlowFlags.RETURNS);
ifFalseThrows = flow.is(FlowFlags.THROWS);
ifFalseAllocates = flow.is(FlowFlags.ALLOCATES);
flow = flow.leaveBranchOrScope(); flow = flow.leaveBranchOrScope();
currentFunction.flow = flow; 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); return module.createIf(condExpr, ifTrueExpr, ifFalseExpr);
} }
@ -1556,6 +1600,8 @@ export class Compiler extends DiagnosticEmitter {
// nest blocks in order // nest blocks in order
var currentBlock = module.createBlock("case0|" + context, breaks, NativeType.None); var currentBlock = module.createBlock("case0|" + context, breaks, NativeType.None);
var alwaysReturns = true; var alwaysReturns = true;
var alwaysThrows = true;
var alwaysAllocates = true;
for (let i = 0; i < numCases; ++i) { for (let i = 0; i < numCases; ++i) {
let case_ = cases[i]; let case_ = cases[i];
let statements = case_.statements; let statements = case_.statements;
@ -1577,6 +1623,12 @@ export class Compiler extends DiagnosticEmitter {
if (!(fallsThrough || flow.is(FlowFlags.RETURNS))) { if (!(fallsThrough || flow.is(FlowFlags.RETURNS))) {
alwaysReturns = false; // ignore fall-throughs 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 // Switch back to the parent flow
currentFunction.flow = flow.leaveBranchOrScope(); currentFunction.flow = flow.leaveBranchOrScope();
@ -1586,9 +1638,11 @@ export class Compiler extends DiagnosticEmitter {
currentFunction.leaveBreakContext(); currentFunction.leaveBreakContext();
// If the switch has a default and always returns, propagate that // If the switch has a default and always returns, propagate that
if (defaultIndex >= 0 && alwaysReturns) { if (defaultIndex >= 0) {
currentFunction.flow.set(FlowFlags.RETURNS); let flow = currentFunction.flow;
// Binaryen understands that so we don't need a hint if (alwaysReturns) flow.set(FlowFlags.RETURNS);
if (alwaysThrows) flow.set(FlowFlags.THROWS);
if (alwaysAllocates) flow.set(FlowFlags.ALLOCATES);
} }
return currentBlock; return currentBlock;
} }
@ -1596,8 +1650,8 @@ export class Compiler extends DiagnosticEmitter {
compileThrowStatement(statement: ThrowStatement): ExpressionRef { compileThrowStatement(statement: ThrowStatement): ExpressionRef {
var flow = this.currentFunction.flow; var flow = this.currentFunction.flow;
// Remember that this branch possibly throws // Remember that this branch throws
flow.set(FlowFlags.POSSIBLY_THROWS); flow.set(FlowFlags.THROWS);
// FIXME: without try-catch it is safe to assume RETURNS as well for now // FIXME: without try-catch it is safe to assume RETURNS as well for now
flow.set(FlowFlags.RETURNS); flow.set(FlowFlags.RETURNS);
@ -1791,8 +1845,8 @@ export class Compiler extends DiagnosticEmitter {
flow.continueLabel = continueLabel; flow.continueLabel = continueLabel;
var body = this.compileStatement(statement.statement); var body = this.compileStatement(statement.statement);
var alwaysReturns = false && flow.is(FlowFlags.RETURNS); var alwaysReturns = false; // CONDITION_IS_ALWAYS_TRUE && flow.is(FlowFlags.RETURNS);
// TODO: evaluate possible always-true conditions // TODO: evaluate if condition is always true
// Switch back to the parent flow // Switch back to the parent flow
currentFunction.flow = flow.leaveBranchOrScope(); currentFunction.flow = flow.leaveBranchOrScope();
@ -4471,6 +4525,18 @@ export class Compiler extends DiagnosticEmitter {
let parent = assert(currentFunction.memberOf); let parent = assert(currentFunction.memberOf);
assert(parent.kind == ElementKind.CLASS); assert(parent.kind == ElementKind.CLASS);
let thisType = (<Class>parent).type; 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; this.currentType = thisType;
return module.createGetLocal(0, thisType.toNativeType()); return module.createGetLocal(0, thisType.toNativeType());
} }
@ -4908,70 +4974,43 @@ export class Compiler extends DiagnosticEmitter {
var options = this.options; var options = this.options;
var currentFunction = this.currentFunction; var currentFunction = this.currentFunction;
// obtain the class being instantiated
var resolved = this.program.resolveExpression( // reports var resolved = this.program.resolveExpression( // reports
expression.expression, expression.expression,
currentFunction currentFunction
); );
if (resolved) { if (!resolved) return module.createUnreachable();
if (resolved.element.kind == ElementKind.CLASS_PROTOTYPE) { if (resolved.element.kind != ElementKind.CLASS_PROTOTYPE) {
let prototype = <ClassPrototype>resolved.element; this.error(
let instance = prototype.resolveUsingTypeArguments( // reports DiagnosticCode.Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature,
expression.typeArguments, expression.expression.range
null, );
expression return this.module.createUnreachable();
);
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
);
}
} }
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( 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)
)
);
}

View File

@ -2377,8 +2377,10 @@ export class FunctionPrototype extends Element {
} }
var returnType: Type; var returnType: Type;
if (this.is(CommonFlags.SET) || this.is(CommonFlags.CONSTRUCTOR)) { if (this.is(CommonFlags.SET)) {
returnType = Type.void; // not annotated returnType = Type.void; // not annotated
} else if (this.is(CommonFlags.CONSTRUCTOR)) {
returnType = assert(classInstance).type; // not annotated
} else { } else {
let typeNode = assert(signatureNode.returnType); let typeNode = assert(signatureNode.returnType);
let type = this.program.resolveType(typeNode, contextualTypeArguments, true); // reports let type = this.program.resolveType(typeNode, contextualTypeArguments, true); // reports
@ -3157,14 +3159,28 @@ export class Interface extends Class {
export const enum FlowFlags { export const enum FlowFlags {
/** No specific conditions. */ /** No specific conditions. */
NONE = 0, NONE = 0,
/** This branch always returns. */ /** This branch always returns. */
RETURNS = 1 << 0, RETURNS = 1 << 0,
/** This branch possibly throws. */ /** This branch always throws. */
POSSIBLY_THROWS = 1 << 1, THROWS = 1 << 1,
/** This branch possible breaks. */ /** This branch always breaks. */
POSSIBLY_BREAKS = 1 << 2, BREAKS = 1 << 2,
/** This branch possible continues. */ /** This branch always continues. */
POSSIBLY_CONTINUES = 1 << 3 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. */ /** A control flow evaluator. */
@ -3200,16 +3216,18 @@ export class Flow {
is(flag: FlowFlags): bool { return (this.flags & flag) == flag; } is(flag: FlowFlags): bool { return (this.flags & flag) == flag; }
/** Sets the specified flag or flags. */ /** Sets the specified flag or flags. */
set(flag: FlowFlags): void { this.flags |= flag; } 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. */ /** Enters a new branch or scope and returns the new flow. */
enterBranchOrScope(): Flow { enterBranchOrScope(): Flow {
var branchFlow = new Flow(); var branch = new Flow();
branchFlow.parent = this; branch.parent = this;
branchFlow.flags = this.flags; branch.flags = this.flags;
branchFlow.currentFunction = this.currentFunction; branch.currentFunction = this.currentFunction;
branchFlow.continueLabel = this.continueLabel; branch.continueLabel = this.continueLabel;
branchFlow.breakLabel = this.breakLabel; branch.breakLabel = this.breakLabel;
return branchFlow; return branch;
} }
/** Leaves the current branch or scope and returns the parent flow. */ /** Leaves the current branch or scope and returns the parent flow. */
@ -3225,14 +3243,20 @@ export class Flow {
} }
// Propagate flags to parent // Propagate flags to parent
if (this.is(FlowFlags.POSSIBLY_THROWS)) { if (this.is(FlowFlags.RETURNS)) {
parent.set(FlowFlags.POSSIBLY_THROWS); parent.set(FlowFlags.CONDITIONALLY_RETURNS);
} }
if (this.is(FlowFlags.POSSIBLY_BREAKS) && parent.breakLabel == this.breakLabel) { if (this.is(FlowFlags.THROWS)) {
parent.set(FlowFlags.POSSIBLY_BREAKS); parent.set(FlowFlags.CONDITIONALLY_THROWS);
} }
if (this.is(FlowFlags.POSSIBLY_CONTINUES) && parent.continueLabel == this.continueLabel) { if (this.is(FlowFlags.BREAKS) && parent.breakLabel == this.breakLabel) {
parent.set(FlowFlags.POSSIBLY_CONTINUES); 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; return parent;
@ -3265,10 +3289,9 @@ export class Flow {
} }
/** Finalizes this flow. Must be the topmost parent flow of the function. */ /** 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"); assert(this.parent == null, "must be the topmost parent flow");
this.continueLabel = null; this.continueLabel = null;
this.breakLabel = null; this.breakLabel = null;
return this.is(FlowFlags.RETURNS);
} }
} }

12
std/assembly.d.ts vendored
View File

@ -248,7 +248,17 @@ declare function parseI64(str: string, radix?: i32): i64;
/** Parses a string to a 64-bit float. */ /** Parses a string to a 64-bit float. */
declare function parseFloat(str: string): f64; 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`. */ /** Class representing a sequence of values of type `T`. */
declare class Array<T> { declare class Array<T> {

View 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
View File

@ -200,6 +200,16 @@ declare function parseFloat(str: string): f64;
declare const NaN: f32 | f64; declare const NaN: f32 | f64;
declare const Infinity: 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> { declare class Array<T> {
[key: number]: T; [key: number]: T;
length: i32; length: i32;

File diff suppressed because it is too large Load Diff

View 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);

File diff suppressed because it is too large Load Diff

View 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)
)
)
)
)

View 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();

View 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)
)
)
)
)

View File

@ -1,6 +1,6 @@
(module (module
(type $ifi (func (param i32 f32) (result i32)))
(type $ii (func (param i32) (result i32))) (type $ii (func (param i32) (result i32)))
(type $ifv (func (param i32 f32)))
(type $v (func)) (type $v (func))
(global "$(lib)/allocator/arena/startOffset" (mut i32) (i32.const 0)) (global "$(lib)/allocator/arena/startOffset" (mut i32) (i32.const 0))
(global "$(lib)/allocator/arena/offset" (mut i32) (i32.const 0)) (global "$(lib)/allocator/arena/offset" (mut i32) (i32.const 0))
@ -91,12 +91,33 @@
) )
(get_local $1) (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 (i32.store
(get_local $0) (get_local $0)
(i32.add (i32.add
(i32.load (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) (i32.const 1)
) )
@ -105,9 +126,9 @@
(get_local $0) (get_local $0)
(get_local $1) (get_local $1)
) )
(get_local $0)
) )
(func $start (; 2 ;) (type $v) (func $start (; 2 ;) (type $v)
(local $0 i32)
(set_global "$(lib)/allocator/arena/startOffset" (set_global "$(lib)/allocator/arena/startOffset"
(i32.and (i32.and
(i32.add (i32.add
@ -121,24 +142,9 @@
(get_global "$(lib)/allocator/arena/startOffset") (get_global "$(lib)/allocator/arena/startOffset")
) )
(set_global $std/new/aClass (set_global $std/new/aClass
(block (result i32) (call $std/new/AClass#constructor
(i32.store (i32.const 0)
(tee_local $0 (f32.const 3)
(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)
) )
) )
) )

View File

@ -1,7 +1,7 @@
(module (module
(type $i (func (result i32))) (type $i (func (result i32)))
(type $ifi (func (param i32 f32) (result i32)))
(type $ii (func (param i32) (result i32))) (type $ii (func (param i32) (result i32)))
(type $ifv (func (param i32 f32)))
(type $v (func)) (type $v (func))
(global "$(lib)/allocator/common/alignment/BITS" i32 (i32.const 3)) (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/SIZE" i32 (i32.const 8))
@ -116,23 +116,49 @@
(get_local $1) (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)
(i32.store (local $2 i32)
(get_local $0) (block
(i32.add (i32.store
(i32.load (get_local $0)
(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 $0)
(get_local $1)
)
) )
(func $start (; 2 ;) (type $v) (func $start (; 2 ;) (type $v)
(local $0 i32)
(set_global "$(lib)/allocator/arena/startOffset" (set_global "$(lib)/allocator/arena/startOffset"
(i32.and (i32.and
(i32.add (i32.add
@ -149,25 +175,9 @@
(get_global "$(lib)/allocator/arena/startOffset") (get_global "$(lib)/allocator/arena/startOffset")
) )
(set_global $std/new/aClass (set_global $std/new/aClass
(block (result i32) (call $std/new/AClass#constructor
(set_local $0 (i32.const 0)
(call "$(lib)/allocator/arena/allocate_memory" (f32.const 3)
(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)
) )
) )
) )