Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix / Enhancement for Omitted Object Literals #1229

Merged
merged 10 commits into from
Apr 21, 2020
131 changes: 120 additions & 11 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8682,12 +8682,26 @@ export class Compiler extends DiagnosticEmitter {
var values = expression.values;
var members = classReference.members;
var hasErrors = false;
var exprs = new Array<ExpressionRef>(numNames + 2);
var exprs = new Array<ExpressionRef>();
var flow = this.currentFlow;
var tempLocal = isManaged
? flow.getAutoreleaseLocal(classReference.type)
: flow.getTempLocal(classReference.type);
assert(numNames == values.length);

// Assume all class fields will be omitted, and add them to our omitted list
let omittedClassFieldMembers = new Map<string, string>();
dcodeIO marked this conversation as resolved.
Show resolved Hide resolved
if (members) {
for (let _keys = Map_keys(members), i = 0, k = _keys.length; i < k; ++i) {
let memberKey = _keys[i];
let member = assert(members.get(memberKey));
if (member !== null && member.kind == ElementKind.FIELD) {
omittedClassFieldMembers.set(member.name, '');
dcodeIO marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

// Iterate through the members definted in our expression
dcodeIO marked this conversation as resolved.
Show resolved Hide resolved
for (let i = 0, k = numNames; i < k; ++i) {
let member = members ? members.get(names[i].text) : null;
if (!member || member.kind != ElementKind.FIELD) {
Expand All @@ -8700,27 +8714,122 @@ export class Compiler extends DiagnosticEmitter {
}
let fieldInstance = <Field>member;
let fieldType = fieldInstance.type;
exprs[i + 1] = this.module.store( // TODO: handle setters as well
fieldType.byteSize,
this.module.local_get(tempLocal.index, this.options.nativeSizeType),
this.compileExpression(values[i], fieldInstance.type, Constraints.CONV_IMPLICIT),
fieldType.toNativeType(),
fieldInstance.memoryOffset
);
exprs.push(this.module.store( // TODO: handle setters as well
fieldType.byteSize,
this.module.local_get(tempLocal.index, this.options.nativeSizeType),
this.compileExpression(values[i], fieldInstance.type, Constraints.CONV_IMPLICIT),
fieldType.toNativeType(),
fieldInstance.memoryOffset
));
dcodeIO marked this conversation as resolved.
Show resolved Hide resolved

// This member is no longer omitted, so delete from our omitted fields
omittedClassFieldMembers.delete(member.name);
}
this.currentType = classReference.type.nonNullableType;
if (hasErrors) return module.unreachable();

// Iterate through the remaining omittedClassFieldMembers.
if (members) {
for(let _keys = Map_keys(omittedClassFieldMembers), i = 0, k = _keys.length; i < k; ++i) {
let omittedMemberKey = _keys[i];
let member = assert(members.get(omittedMemberKey));

let fieldInstance = <Field>member;
let fieldType = fieldInstance.type;

if (fieldType.is(TypeFlags.REFERENCE) && fieldType.classReference !== null) {
// TODO: Check if it is a class, with a default value (constructor with no params).
// TODO: Check if it can be null, and set to null

// Otherwise, error
this.error(
DiagnosticCode.Object_literal_is_missing_class_member_fields_that_must_be_defined,
dcodeIO marked this conversation as resolved.
Show resolved Hide resolved
expression.range, classReference.toString()
);
hasErrors = true;
continue;
}

switch(fieldType.kind) {
// i32 Types
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.I32:
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.USIZE:
case TypeKind.ISIZE:
case TypeKind.BOOL: {
exprs.push(this.module.store( // TODO: handle setters as well
fieldType.byteSize,
this.module.local_get(tempLocal.index, this.options.nativeSizeType),
this.module.i32(0),
dcodeIO marked this conversation as resolved.
Show resolved Hide resolved
fieldType.toNativeType(),
fieldInstance.memoryOffset
));
continue;
}

// i64 Types
case TypeKind.I64:
case TypeKind.U64: {
exprs.push(this.module.store( // TODO: handle setters as well
fieldType.byteSize,
this.module.local_get(tempLocal.index, this.options.nativeSizeType),
this.module.i64(0),
fieldType.toNativeType(),
fieldInstance.memoryOffset
));
continue;
}

// f32 Types
case TypeKind.F32: {
exprs.push(this.module.store( // TODO: handle setters as well
fieldType.byteSize,
this.module.local_get(tempLocal.index, this.options.nativeSizeType),
this.module.f32(0),
fieldType.toNativeType(),
fieldInstance.memoryOffset
));
continue;
}

// f64 Types
case TypeKind.F64: {
exprs.push(this.module.store( // TODO: handle setters as well
fieldType.byteSize,
this.module.local_get(tempLocal.index, this.options.nativeSizeType),
this.module.f64(0),
fieldType.toNativeType(),
fieldInstance.memoryOffset
));
continue;
}
default: {}
dcodeIO marked this conversation as resolved.
Show resolved Hide resolved
}

// Otherwise, error
this.error(
DiagnosticCode.Object_literal_is_missing_class_member_fields_that_must_be_defined,
expression.range, classReference.toString()
);
hasErrors = true;
}
}
if (hasErrors) return module.unreachable();

// allocate a new instance first and assign 'this' to the temp. local
exprs[0] = module.local_set(
exprs.unshift(module.local_set(
tempLocal.index,
isManaged
? this.makeRetain(this.makeAllocation(classReference))
: this.makeAllocation(classReference)
);
));

// once all field values have been set, return 'this'
exprs[exprs.length - 1] = module.local_get(tempLocal.index, this.options.nativeSizeType);
exprs.push(module.local_get(tempLocal.index, this.options.nativeSizeType));

if (!isManaged) flow.freeTempLocal(tempLocal);
this.currentType = classReference.type;
Expand Down
2 changes: 2 additions & 0 deletions src/diagnosticMessages.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export enum DiagnosticCode {
_0_is_not_a_valid_operator = 224,
Expression_cannot_be_represented_by_a_type = 225,
Expression_resolves_to_unusual_type_0 = 226,
Object_literal_is_missing_class_member_fields_that_must_be_defined = 227,
Type_0_is_cyclic_Module_will_include_deferred_garbage_collection = 900,
Importing_the_table_disables_some_indirect_call_optimizations = 901,
Exporting_the_table_disables_some_indirect_call_optimizations = 902,
Expand Down Expand Up @@ -194,6 +195,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 224: return "'{0}' is not a valid operator.";
case 225: return "Expression cannot be represented by a type.";
case 226: return "Expression resolves to unusual type '{0}'.";
case 227: return "Object literal is missing class member fields that must be defined";
case 900: return "Type '{0}' is cyclic. Module will include deferred garbage collection.";
case 901: return "Importing the table disables some indirect call optimizations.";
case 902: return "Exporting the table disables some indirect call optimizations.";
Expand Down
1 change: 1 addition & 0 deletions src/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"'{0}' is not a valid operator.": 224,
"Expression cannot be represented by a type.": 225,
"Expression resolves to unusual type '{0}'.": 226,
"Object literal is missing class member fields that must be defined": 227,

"Type '{0}' is cyclic. Module will include deferred garbage collection.": 900,
"Importing the table disables some indirect call optimizations.": 901,
Expand Down
5 changes: 5 additions & 0 deletions tests/compiler/std/object-literal-omitted.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"asc_flags": [
"--runtime full"
]
}
Loading