Skip to content

Commit 1b0ed61

Browse files
committed
Field initializers and constructors
1 parent b1e7b75 commit 1b0ed61

File tree

10 files changed

+516
-162
lines changed

10 files changed

+516
-162
lines changed

src/compiler.ts

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2652,8 +2652,11 @@ export class Compiler extends DiagnosticEmitter {
26522652
return this.module.createUnreachable();
26532653
}
26542654

2655-
/** Compiles a call to a function. If an instance method, `this` is the first element in `argumentExpressions`. */
2656-
compileCall(functionInstance: Function, argumentExpressions: Expression[], reportNode: Node): ExpressionRef {
2655+
/**
2656+
* Compiles a call to a function. If an instance method, `this` is the first element in
2657+
* `argumentExpressions` or can be specified explicitly as the last argument.
2658+
*/
2659+
compileCall(functionInstance: Function, argumentExpressions: Expression[], reportNode: Node, thisArg: ExpressionRef = 0): ExpressionRef {
26572660

26582661
// validate and compile arguments
26592662
var parameters = functionInstance.parameters;
@@ -2662,6 +2665,8 @@ export class Compiler extends DiagnosticEmitter {
26622665
var numParametersInclThis = functionInstance.instanceMethodOf != null ? numParameters + 1 : numParameters;
26632666
var numArgumentsInclThis = argumentExpressions.length;
26642667
var numArguments = functionInstance.instanceMethodOf != null ? numArgumentsInclThis - 1 : numArgumentsInclThis;
2668+
if (thisArg)
2669+
numArgumentsInclThis++;
26652670

26662671
if (numArgumentsInclThis > numParametersInclThis) { // too many arguments
26672672
this.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range,
@@ -2672,13 +2677,18 @@ export class Compiler extends DiagnosticEmitter {
26722677
}
26732678
var operands = new Array<ExpressionRef>(numParametersInclThis);
26742679
var operandIndex = 0;
2675-
if (functionInstance.instanceMethodOf)
2676-
operands[operandIndex++] = this.compileExpression(argumentExpressions[0], functionInstance.instanceMethodOf.type);
2680+
var argumentIndex = 0;
2681+
if (functionInstance.instanceMethodOf) {
2682+
if (thisArg)
2683+
operands[operandIndex++] = thisArg;
2684+
else
2685+
operands[operandIndex++] = this.compileExpression(argumentExpressions[argumentIndex++], functionInstance.instanceMethodOf.type);
2686+
}
26772687
for (; operandIndex < numParametersInclThis; ++operandIndex) {
26782688

26792689
// argument has been provided
26802690
if (numArgumentsInclThis > operandIndex) {
2681-
operands[operandIndex] = this.compileExpression(argumentExpressions[operandIndex], parameters[operandIndex + numParameters - numParametersInclThis].type);
2691+
operands[operandIndex] = this.compileExpression(argumentExpressions[argumentIndex++], parameters[operandIndex + numParameters - numParametersInclThis].type);
26822692

26832693
// argument has been omitted
26842694
} else {
@@ -2866,7 +2876,6 @@ export class Compiler extends DiagnosticEmitter {
28662876

28672877
// case LiteralKind.OBJECT:
28682878
// case LiteralKind.REGEXP:
2869-
// case LiteralKind.STRING:
28702879
}
28712880
throw new Error("not implemented");
28722881
}
@@ -2901,9 +2910,46 @@ export class Compiler extends DiagnosticEmitter {
29012910
var prototype = <ClassPrototype>resolved.element;
29022911
var instance = prototype.resolveInclTypeArguments(expression.typeArguments, null, expression); // reports
29032912
if (instance) {
2904-
// TODO: call constructor
2913+
var thisExpr = compileBuiltinAllocate(this, instance, expression);
2914+
var initializers = new Array<ExpressionRef>();
2915+
2916+
// use a temp local for 'this'
2917+
var tempLocal = this.currentFunction.getTempLocal(this.options.usizeType);
2918+
initializers.push(this.module.createSetLocal(tempLocal.index, thisExpr));
2919+
2920+
// apply field initializers
2921+
if (instance.members)
2922+
for (var member of instance.members.values()) {
2923+
if (member.kind == ElementKind.FIELD) {
2924+
var field = <Field>member;
2925+
var fieldDeclaration = field.prototype.declaration;
2926+
if (field.is(ElementFlags.CONSTANT)) {
2927+
assert(false); // there are no built-in fields currently
2928+
} else if (fieldDeclaration && fieldDeclaration.initializer) {
2929+
initializers.push(this.module.createStore(field.type.byteSize,
2930+
this.module.createGetLocal(tempLocal.index, this.options.nativeSizeType),
2931+
this.compileExpression(fieldDeclaration.initializer, field.type),
2932+
field.type.toNativeType(),
2933+
field.memoryOffset
2934+
));
2935+
}
2936+
}
2937+
}
2938+
2939+
// apply constructor
2940+
var constructorInstance = instance.constructorInstance;
2941+
if (constructorInstance)
2942+
initializers.push(this.compileCall(constructorInstance, expression.arguments, expression,
2943+
this.module.createGetLocal(tempLocal.index, this.options.nativeSizeType)
2944+
));
2945+
2946+
// return 'this'
2947+
initializers.push(this.module.createGetLocal(tempLocal.index, this.options.nativeSizeType));
2948+
this.currentFunction.freeTempLocal(tempLocal);
2949+
thisExpr = this.module.createBlock(null, initializers, this.options.nativeSizeType);
2950+
29052951
this.currentType = instance.type;
2906-
return compileBuiltinAllocate(this, instance, expression);
2952+
return thisExpr;
29072953
}
29082954
} else
29092955
this.error(DiagnosticCode.Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature, expression.expression.range);

src/program.ts

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -436,13 +436,15 @@ export class Program extends DiagnosticEmitter {
436436
// if (classPrototype.isUnmanaged && instancePrototype.isAbstract) {
437437
// this.error( Unmanaged classes cannot declare abstract methods. );
438438
// }
439-
classPrototype.instanceMembers.set(name, prototype);
440439
if (declaration.name.kind == NodeKind.CONSTRUCTOR) {
441440
if (classPrototype.constructorPrototype)
442441
this.error(DiagnosticCode.Multiple_constructor_implementations_are_not_allowed, declaration.name.range);
443-
else
442+
else {
443+
prototype.set(ElementFlags.CONSTRUCTOR);
444444
classPrototype.constructorPrototype = prototype;
445-
}
445+
}
446+
} else
447+
classPrototype.instanceMembers.set(name, prototype);
446448
}
447449

448450
this.checkOperators(declaration.decorators, prototype, classPrototype);
@@ -1075,7 +1077,6 @@ export class Program extends DiagnosticEmitter {
10751077
case ElementKind.LOCAL:
10761078
case ElementKind.FIELD:
10771079
if (!(targetType = (<VariableLikeElement>target).type).classType) {
1078-
console.log(propertyAccess.property.name + " on " + targetType);
10791080
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, targetType.toString());
10801081
return null;
10811082
}
@@ -1282,10 +1283,10 @@ export enum ElementFlags {
12821283
PRIVATE = 1 << 15,
12831284
/** Is an abstract member. */
12841285
ABSTRACT = 1 << 16,
1286+
/** Is a constructor. */
1287+
CONSTRUCTOR = 1 << 17,
12851288
/** Is an unmanaged class with limited capabilites. */
1286-
UNMANAGED = 1 << 17,
1287-
/** Has already inherited base class static members. */
1288-
HAS_STATIC_BASE_MEMBERS = 1 << 18,
1289+
UNMANAGED = 1 << 18,
12891290
/** Is scoped. */
12901291
SCOPED = 1 << 19,
12911292
/** Is the start function. */
@@ -1524,17 +1525,6 @@ export class FunctionPrototype extends Element {
15241525
this.set(ElementFlags.INSTANCE);
15251526
}
15261527

1527-
/** Whether a getter function or not. */
1528-
get isGetter(): bool { return (this.flags & ElementFlags.GETTER) != 0; }
1529-
set isGetter(is: bool) { if (is) this.flags |= ElementFlags.GETTER; else this.flags &= ~ElementFlags.GETTER; }
1530-
1531-
/** Whether a setter function or not. */
1532-
get isSetter(): bool { return (this.flags & ElementFlags.SETTER) != 0; }
1533-
set isSetter(is: bool) { if (is) this.flags |= ElementFlags.SETTER; else this.flags &= ~ElementFlags.SETTER; }
1534-
1535-
// Whether a getter/setter function or not.
1536-
get isAccessor(): bool { return (this.flags & (ElementFlags.GETTER | ElementFlags.SETTER)) != 0; }
1537-
15381528
resolve(functionTypeArguments: Type[] | null = null, contextualTypeArguments: Map<string,Type> | null = null): Function | null {
15391529
var instanceKey = functionTypeArguments ? typesToString(functionTypeArguments) : "";
15401530
var instance = this.instances.get(instanceKey);
@@ -1584,29 +1574,11 @@ export class FunctionPrototype extends Element {
15841574
var parameterTypes = new Array<Type>(k);
15851575
var typeNode: TypeNode | null;
15861576
for (i = 0; i < k; ++i) {
1587-
if (typeNode = declaration.parameters[i].type) {
1588-
var parameterType = this.program.resolveType(typeNode, contextualTypeArguments, true); // reports
1589-
if (parameterType) {
1590-
parameters[i] = new Parameter(declaration.parameters[i].name.name, parameterType, declaration.parameters[i].initializer);
1591-
parameterTypes[i] = parameterType;
1592-
} else
1593-
return null;
1594-
} else
1595-
return null;
1596-
}
1597-
1598-
// resolve return type
1599-
// TODO: 'this' type
1600-
var returnType: Type;
1601-
if (this.isSetter) {
1602-
returnType = Type.void; // not annotated
1603-
} else {
1604-
if (typeNode = declaration.returnType) {
1605-
var type = this.program.resolveType(<TypeNode>typeNode, contextualTypeArguments, true); // reports
1606-
if (type)
1607-
returnType = type;
1608-
else
1609-
return null;
1577+
typeNode = assert(declaration.parameters[i].type);
1578+
var parameterType = this.program.resolveType(typeNode, contextualTypeArguments, true); // reports
1579+
if (parameterType) {
1580+
parameters[i] = new Parameter(declaration.parameters[i].name.name, parameterType, declaration.parameters[i].initializer);
1581+
parameterTypes[i] = parameterType;
16101582
} else
16111583
return null;
16121584
}
@@ -1620,6 +1592,21 @@ export class FunctionPrototype extends Element {
16201592
if (!classInstance)
16211593
return null;
16221594
}
1595+
1596+
// resolve return type
1597+
// TODO: 'this' type
1598+
var returnType: Type;
1599+
if (this.is(ElementFlags.SETTER) || this.is(ElementFlags.CONSTRUCTOR)) {
1600+
returnType = Type.void; // not annotated
1601+
} else {
1602+
typeNode = assert(declaration.returnType);
1603+
var type = this.program.resolveType(<TypeNode>typeNode, contextualTypeArguments, true); // reports
1604+
if (type)
1605+
returnType = type;
1606+
else
1607+
return null;
1608+
}
1609+
16231610
instance = new Function(this, internalName, functionTypeArguments, parameters, returnType, classInstance);
16241611
instance.contextualTypeArguments = contextualTypeArguments;
16251612
this.instances.set(instanceKey, instance);
@@ -1965,8 +1952,7 @@ export class ClassPrototype extends Element {
19651952
this.program.error(DiagnosticCode.Structs_cannot_extend_classes_and_vice_versa, Range.join(declaration.name.range, declaration.extendsType.range));
19661953
return null;
19671954
}
1968-
} else
1969-
this.flags |= ElementFlags.HAS_STATIC_BASE_MEMBERS; // fwiw
1955+
}
19701956

19711957
// override call specific contextual type arguments if provided
19721958
var i: i32, k: i32;
@@ -1996,6 +1982,13 @@ export class ClassPrototype extends Element {
19961982
}
19971983
}
19981984

1985+
if (this.constructorPrototype) {
1986+
var partialConstructor = this.constructorPrototype.resolvePartial(typeArguments); // reports
1987+
if (partialConstructor)
1988+
instance.constructorInstance = partialConstructor.resolve(); // reports
1989+
// TODO: ^ doesn't know the return type, hence returns null
1990+
}
1991+
19991992
if (this.instanceMembers)
20001993
for (var member of this.instanceMembers.values()) {
20011994
switch (member.kind) {
@@ -2086,6 +2079,8 @@ export class Class extends Element {
20862079
contextualTypeArguments: Map<string,Type> | null = null;
20872080
/** Current member memory offset. */
20882081
currentMemoryOffset: u32 = 0;
2082+
/** Constructor instance. */
2083+
constructorInstance: Function | null = null;
20892084

20902085
/** Constructs a new class. */
20912086
constructor(prototype: ClassPrototype, internalName: string, typeArguments: Type[] | null = null, base: Class | null = null) {

std/assembly.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ declare class String {
237237
concat(other: string): string;
238238
endsWith(other: string): bool;
239239
indexOf(other: string): u32;
240+
includes(other: string): bool;
240241
startsWith(other: string): bool;
241242
substr(start: u32, length?: u32): string;
242243
substring(start: u32, end?: u32): string;

std/portable.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ declare class String {
176176
readonly length: i32;
177177
private constructor();
178178
indexOf(subject: string): i32;
179+
includes(other: string): bool;
179180
lastIndexOf(subject: string): i32;
180181
charAt(index: i32): string;
181182
charCodeAt(index: i32): i32;

tests/compiler/new.optimized.wast

Lines changed: 0 additions & 20 deletions
This file was deleted.

tests/compiler/new.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/compiler/new.wast

Lines changed: 0 additions & 76 deletions
This file was deleted.

0 commit comments

Comments
 (0)