Field initializers and constructors

This commit is contained in:
dcodeIO
2018-01-28 15:13:31 +01:00
parent b1e7b75ad7
commit 1b0ed61072
10 changed files with 516 additions and 162 deletions

View File

@ -2652,8 +2652,11 @@ export class Compiler extends DiagnosticEmitter {
return this.module.createUnreachable();
}
/** Compiles a call to a function. If an instance method, `this` is the first element in `argumentExpressions`. */
compileCall(functionInstance: Function, argumentExpressions: Expression[], reportNode: Node): ExpressionRef {
/**
* Compiles a call to a function. If an instance method, `this` is the first element in
* `argumentExpressions` or can be specified explicitly as the last argument.
*/
compileCall(functionInstance: Function, argumentExpressions: Expression[], reportNode: Node, thisArg: ExpressionRef = 0): ExpressionRef {
// validate and compile arguments
var parameters = functionInstance.parameters;
@ -2662,6 +2665,8 @@ export class Compiler extends DiagnosticEmitter {
var numParametersInclThis = functionInstance.instanceMethodOf != null ? numParameters + 1 : numParameters;
var numArgumentsInclThis = argumentExpressions.length;
var numArguments = functionInstance.instanceMethodOf != null ? numArgumentsInclThis - 1 : numArgumentsInclThis;
if (thisArg)
numArgumentsInclThis++;
if (numArgumentsInclThis > numParametersInclThis) { // too many arguments
this.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range,
@ -2672,13 +2677,18 @@ export class Compiler extends DiagnosticEmitter {
}
var operands = new Array<ExpressionRef>(numParametersInclThis);
var operandIndex = 0;
if (functionInstance.instanceMethodOf)
operands[operandIndex++] = this.compileExpression(argumentExpressions[0], functionInstance.instanceMethodOf.type);
var argumentIndex = 0;
if (functionInstance.instanceMethodOf) {
if (thisArg)
operands[operandIndex++] = thisArg;
else
operands[operandIndex++] = this.compileExpression(argumentExpressions[argumentIndex++], functionInstance.instanceMethodOf.type);
}
for (; operandIndex < numParametersInclThis; ++operandIndex) {
// argument has been provided
if (numArgumentsInclThis > operandIndex) {
operands[operandIndex] = this.compileExpression(argumentExpressions[operandIndex], parameters[operandIndex + numParameters - numParametersInclThis].type);
operands[operandIndex] = this.compileExpression(argumentExpressions[argumentIndex++], parameters[operandIndex + numParameters - numParametersInclThis].type);
// argument has been omitted
} else {
@ -2866,7 +2876,6 @@ export class Compiler extends DiagnosticEmitter {
// case LiteralKind.OBJECT:
// case LiteralKind.REGEXP:
// case LiteralKind.STRING:
}
throw new Error("not implemented");
}
@ -2901,9 +2910,46 @@ export class Compiler extends DiagnosticEmitter {
var prototype = <ClassPrototype>resolved.element;
var instance = prototype.resolveInclTypeArguments(expression.typeArguments, null, expression); // reports
if (instance) {
// TODO: call constructor
var thisExpr = compileBuiltinAllocate(this, instance, expression);
var initializers = new Array<ExpressionRef>();
// use a temp local for 'this'
var tempLocal = this.currentFunction.getTempLocal(this.options.usizeType);
initializers.push(this.module.createSetLocal(tempLocal.index, thisExpr));
// apply field initializers
if (instance.members)
for (var member of instance.members.values()) {
if (member.kind == ElementKind.FIELD) {
var field = <Field>member;
var fieldDeclaration = field.prototype.declaration;
if (field.is(ElementFlags.CONSTANT)) {
assert(false); // there are no built-in fields currently
} else if (fieldDeclaration && fieldDeclaration.initializer) {
initializers.push(this.module.createStore(field.type.byteSize,
this.module.createGetLocal(tempLocal.index, this.options.nativeSizeType),
this.compileExpression(fieldDeclaration.initializer, field.type),
field.type.toNativeType(),
field.memoryOffset
));
}
}
}
// apply constructor
var constructorInstance = instance.constructorInstance;
if (constructorInstance)
initializers.push(this.compileCall(constructorInstance, expression.arguments, expression,
this.module.createGetLocal(tempLocal.index, this.options.nativeSizeType)
));
// return 'this'
initializers.push(this.module.createGetLocal(tempLocal.index, this.options.nativeSizeType));
this.currentFunction.freeTempLocal(tempLocal);
thisExpr = this.module.createBlock(null, initializers, this.options.nativeSizeType);
this.currentType = instance.type;
return compileBuiltinAllocate(this, instance, expression);
return thisExpr;
}
} else
this.error(DiagnosticCode.Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature, expression.expression.range);

View File

@ -436,13 +436,15 @@ export class Program extends DiagnosticEmitter {
// if (classPrototype.isUnmanaged && instancePrototype.isAbstract) {
// this.error( Unmanaged classes cannot declare abstract methods. );
// }
classPrototype.instanceMembers.set(name, prototype);
if (declaration.name.kind == NodeKind.CONSTRUCTOR) {
if (classPrototype.constructorPrototype)
this.error(DiagnosticCode.Multiple_constructor_implementations_are_not_allowed, declaration.name.range);
else
else {
prototype.set(ElementFlags.CONSTRUCTOR);
classPrototype.constructorPrototype = prototype;
}
}
} else
classPrototype.instanceMembers.set(name, prototype);
}
this.checkOperators(declaration.decorators, prototype, classPrototype);
@ -1075,7 +1077,6 @@ export class Program extends DiagnosticEmitter {
case ElementKind.LOCAL:
case ElementKind.FIELD:
if (!(targetType = (<VariableLikeElement>target).type).classType) {
console.log(propertyAccess.property.name + " on " + targetType);
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, targetType.toString());
return null;
}
@ -1282,10 +1283,10 @@ export enum ElementFlags {
PRIVATE = 1 << 15,
/** Is an abstract member. */
ABSTRACT = 1 << 16,
/** Is a constructor. */
CONSTRUCTOR = 1 << 17,
/** Is an unmanaged class with limited capabilites. */
UNMANAGED = 1 << 17,
/** Has already inherited base class static members. */
HAS_STATIC_BASE_MEMBERS = 1 << 18,
UNMANAGED = 1 << 18,
/** Is scoped. */
SCOPED = 1 << 19,
/** Is the start function. */
@ -1524,17 +1525,6 @@ export class FunctionPrototype extends Element {
this.set(ElementFlags.INSTANCE);
}
/** Whether a getter function or not. */
get isGetter(): bool { return (this.flags & ElementFlags.GETTER) != 0; }
set isGetter(is: bool) { if (is) this.flags |= ElementFlags.GETTER; else this.flags &= ~ElementFlags.GETTER; }
/** Whether a setter function or not. */
get isSetter(): bool { return (this.flags & ElementFlags.SETTER) != 0; }
set isSetter(is: bool) { if (is) this.flags |= ElementFlags.SETTER; else this.flags &= ~ElementFlags.SETTER; }
// Whether a getter/setter function or not.
get isAccessor(): bool { return (this.flags & (ElementFlags.GETTER | ElementFlags.SETTER)) != 0; }
resolve(functionTypeArguments: Type[] | null = null, contextualTypeArguments: Map<string,Type> | null = null): Function | null {
var instanceKey = functionTypeArguments ? typesToString(functionTypeArguments) : "";
var instance = this.instances.get(instanceKey);
@ -1584,29 +1574,11 @@ export class FunctionPrototype extends Element {
var parameterTypes = new Array<Type>(k);
var typeNode: TypeNode | null;
for (i = 0; i < k; ++i) {
if (typeNode = declaration.parameters[i].type) {
var parameterType = this.program.resolveType(typeNode, contextualTypeArguments, true); // reports
if (parameterType) {
parameters[i] = new Parameter(declaration.parameters[i].name.name, parameterType, declaration.parameters[i].initializer);
parameterTypes[i] = parameterType;
} else
return null;
} else
return null;
}
// resolve return type
// TODO: 'this' type
var returnType: Type;
if (this.isSetter) {
returnType = Type.void; // not annotated
} else {
if (typeNode = declaration.returnType) {
var type = this.program.resolveType(<TypeNode>typeNode, contextualTypeArguments, true); // reports
if (type)
returnType = type;
else
return null;
typeNode = assert(declaration.parameters[i].type);
var parameterType = this.program.resolveType(typeNode, contextualTypeArguments, true); // reports
if (parameterType) {
parameters[i] = new Parameter(declaration.parameters[i].name.name, parameterType, declaration.parameters[i].initializer);
parameterTypes[i] = parameterType;
} else
return null;
}
@ -1620,6 +1592,21 @@ export class FunctionPrototype extends Element {
if (!classInstance)
return null;
}
// resolve return type
// TODO: 'this' type
var returnType: Type;
if (this.is(ElementFlags.SETTER) || this.is(ElementFlags.CONSTRUCTOR)) {
returnType = Type.void; // not annotated
} else {
typeNode = assert(declaration.returnType);
var type = this.program.resolveType(<TypeNode>typeNode, contextualTypeArguments, true); // reports
if (type)
returnType = type;
else
return null;
}
instance = new Function(this, internalName, functionTypeArguments, parameters, returnType, classInstance);
instance.contextualTypeArguments = contextualTypeArguments;
this.instances.set(instanceKey, instance);
@ -1965,8 +1952,7 @@ export class ClassPrototype extends Element {
this.program.error(DiagnosticCode.Structs_cannot_extend_classes_and_vice_versa, Range.join(declaration.name.range, declaration.extendsType.range));
return null;
}
} else
this.flags |= ElementFlags.HAS_STATIC_BASE_MEMBERS; // fwiw
}
// override call specific contextual type arguments if provided
var i: i32, k: i32;
@ -1996,6 +1982,13 @@ export class ClassPrototype extends Element {
}
}
if (this.constructorPrototype) {
var partialConstructor = this.constructorPrototype.resolvePartial(typeArguments); // reports
if (partialConstructor)
instance.constructorInstance = partialConstructor.resolve(); // reports
// TODO: ^ doesn't know the return type, hence returns null
}
if (this.instanceMembers)
for (var member of this.instanceMembers.values()) {
switch (member.kind) {
@ -2086,6 +2079,8 @@ export class Class extends Element {
contextualTypeArguments: Map<string,Type> | null = null;
/** Current member memory offset. */
currentMemoryOffset: u32 = 0;
/** Constructor instance. */
constructorInstance: Function | null = null;
/** Constructs a new class. */
constructor(prototype: ClassPrototype, internalName: string, typeArguments: Type[] | null = null, base: Class | null = null) {