Skip to content

Commit 0fd4db2

Browse files
authored
Make class fields a special kind of property (#2548)
BREAKING CHANGE: Element kinds FIELD and FIELD_PROTOTYPE and elements Field and FieldPrototype have been removed. Instead, Property inherit previous Field functionality, indicated by property.isField.
1 parent 5d18a71 commit 0fd4db2

File tree

174 files changed

+54507
-43340
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

174 files changed

+54507
-43340
lines changed

Diff for: package-lock.json

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
},
2626
"engineStrict": true,
2727
"dependencies": {
28-
"binaryen": "110.0.0-nightly.20221019",
28+
"binaryen": "110.0.0-nightly.20221105",
2929
"long": "^5.2.0"
3030
},
3131
"devDependencies": {

Diff for: src/ast.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1622,6 +1622,15 @@ export const enum SourceKind {
16221622

16231623
/** A top-level source node. */
16241624
export class Source extends Node {
1625+
1626+
/** Gets the special native source. */
1627+
static get native(): Source {
1628+
let source = Source._native;
1629+
if (!source) Source._native = source = new Source(SourceKind.LibraryEntry, LIBRARY_PREFIX + "native.ts", "[native code]");
1630+
return source;
1631+
}
1632+
private static _native: Source | null = null;
1633+
16251634
constructor(
16261635
/** Source kind. */
16271636
public sourceKind: SourceKind,

Diff for: src/bindings/js.ts

+15-14
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
LiteralExpression,
66
StringLiteralExpression,
77
TemplateLiteralExpression,
8-
findDecorator
8+
findDecorator,
9+
Source
910
} from "../ast";
1011

1112
import {
@@ -22,7 +23,7 @@ import {
2223
Interface,
2324
Enum,
2425
EnumValue,
25-
Field
26+
PropertyPrototype
2627
} from "../program";
2728

2829
import {
@@ -446,10 +447,6 @@ export class JSBuilder extends ExportsWalker {
446447
this.visitClass(name, element);
447448
}
448449

449-
visitField(name: string, element: Field): void {
450-
// not implemented
451-
}
452-
453450
visitNamespace(name: string, element: Element): void {
454451
// not implemented
455452
}
@@ -1241,12 +1238,14 @@ export class JSBuilder extends ExportsWalker {
12411238
for (let _keys = Map_keys(members), i = 0, k = _keys.length; i < k; ++i) {
12421239
let memberName = _keys[i];
12431240
let member = assert(members.get(memberName));
1244-
if (member.kind != ElementKind.Field) continue;
1245-
let field = <Field>member;
1241+
if (member.kind != ElementKind.PropertyPrototype) continue;
1242+
let property = (<PropertyPrototype>member).instance; // resolved during class finalization
1243+
if (!property || !property.isField) continue;
1244+
assert(property.memoryOffset >= 0);
12461245
indent(sb, this.indentLevel);
1247-
sb.push(field.name);
1246+
sb.push(property.name);
12481247
sb.push(": ");
1249-
this.makeLiftFromMemory(field.type, sb, "pointer + " + field.memoryOffset.toString());
1248+
this.makeLiftFromMemory(property.type, sb, "pointer + " + property.memoryOffset.toString());
12501249
sb.push(",\n");
12511250
}
12521251
}
@@ -1283,10 +1282,12 @@ export class JSBuilder extends ExportsWalker {
12831282
for (let _keys = Map_keys(members), i = 0, k = _keys.length; i < k; ++i) {
12841283
let memberName = _keys[i];
12851284
let member = assert(members.get(memberName));
1286-
if (member.kind != ElementKind.Field) continue;
1287-
let field = <Field>member;
1285+
if (member.kind != ElementKind.PropertyPrototype) continue;
1286+
let property = (<PropertyPrototype>member).instance; // resolved during class finalization
1287+
if (!property || !property.isField) continue;
1288+
assert(property.memoryOffset >= 0);
12881289
indent(sb, this.indentLevel);
1289-
this.makeLowerToMemory(field.type, sb, "pointer + " + field.memoryOffset.toString(), "value." + memberName);
1290+
this.makeLowerToMemory(property.type, sb, "pointer + " + property.memoryOffset.toString(), "value." + memberName);
12901291
sb.push(";\n");
12911292
}
12921293
}
@@ -1343,7 +1344,7 @@ function isPlainObject(clazz: Class): bool {
13431344
if (member.isAny(CommonFlags.Private | CommonFlags.Protected)) return false;
13441345
if (member.is(CommonFlags.Constructor)) {
13451346
// a generated constructor is ok
1346-
if (member.declaration.range != member.program.nativeRange) return false;
1347+
if (member.declaration.range != Source.native.range) return false;
13471348
}
13481349
}
13491350
}

Diff for: src/bindings/tsd.ts

+14-13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import {
2+
Source
3+
} from "../ast";
4+
15
import {
26
CommonFlags
37
} from "../common";
@@ -10,8 +14,8 @@ import {
1014
Interface,
1115
Enum,
1216
ElementKind,
13-
Field,
14-
Element
17+
Element,
18+
PropertyPrototype
1519
} from "../program";
1620

1721
import {
@@ -168,10 +172,6 @@ export class TSDBuilder extends ExportsWalker {
168172
// not implemented
169173
}
170174

171-
visitField(name: string, element: Field): void {
172-
// not implemented
173-
}
174-
175175
visitNamespace(name: string, element: Element): void {
176176
// not implemented
177177
}
@@ -238,7 +238,7 @@ export class TSDBuilder extends ExportsWalker {
238238
if (member.isAny(CommonFlags.Private | CommonFlags.Protected)) return false;
239239
if (member.is(CommonFlags.Constructor)) {
240240
// a generated constructor is ok
241-
if (member.declaration.range != this.program.nativeRange) return false;
241+
if (member.declaration.range != Source.native.range) return false;
242242
}
243243
}
244244
}
@@ -347,15 +347,16 @@ export class TSDBuilder extends ExportsWalker {
347347
for (let _keys = Map_keys(members), i = 0, k = _keys.length; i < k; ++i) {
348348
let memberName = _keys[i];
349349
let member = assert(members.get(memberName));
350-
if (member.kind != ElementKind.Field) continue;
351-
let field = <Field>member;
350+
if (member.kind != ElementKind.PropertyPrototype) continue;
351+
let property = (<PropertyPrototype>member).instance; // resolved during class finalization
352+
if (!property || !property.isField) continue;
352353
sb.push(" /** @type `");
353-
sb.push(field.type.toString());
354+
sb.push(property.type.toString());
354355
sb.push("` */\n ");
355-
sb.push(field.name);
356+
sb.push(property.name);
356357
sb.push(": ");
357-
sb.push(this.toTypeScriptType(field.type, mode));
358-
if (this.fieldAcceptsUndefined(field.type)) {
358+
sb.push(this.toTypeScriptType(property.type, mode));
359+
if (this.fieldAcceptsUndefined(property.type)) {
359360
sb.push(" | TOmittable");
360361
}
361362
sb.push(";\n");

Diff for: src/bindings/util.ts

-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
Enum,
1515
Class,
1616
Interface,
17-
Field,
1817
File,
1918
FunctionPrototype,
2019
Global,
@@ -95,11 +94,6 @@ export abstract class ExportsWalker {
9594
this.visitClassInstances(name, <ClassPrototype>element);
9695
break;
9796
}
98-
case ElementKind.Field: {
99-
let fieldInstance = <Field>element;
100-
if (fieldInstance.is(CommonFlags.Compiled)) this.visitField(name, fieldInstance);
101-
break;
102-
}
10397
case ElementKind.PropertyPrototype: {
10498
let propertyInstance = (<PropertyPrototype>element).instance;
10599
if (!propertyInstance) break;
@@ -150,7 +144,6 @@ export abstract class ExportsWalker {
150144
abstract visitFunction(name: string, element: Function): void;
151145
abstract visitClass(name: string, element: Class): void;
152146
abstract visitInterface(name: string, element: Interface): void;
153-
abstract visitField(name: string, element: Field): void;
154147
abstract visitNamespace(name: string, element: Element): void;
155148
abstract visitAlias(name: string, element: Element, originalName: string): void;
156149
}

Diff for: src/builtins.ts

+33-94
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,10 @@ import {
8080
import {
8181
ElementKind,
8282
FunctionPrototype,
83-
Field,
8483
Global,
8584
DecoratorFlags,
86-
ClassPrototype,
87-
Class
85+
Class,
86+
PropertyPrototype
8887
} from "./program";
8988

9089
import {
@@ -1075,11 +1074,12 @@ function builtin_offsetof(ctx: BuiltinContext): ExpressionRef {
10751074
return module.unreachable();
10761075
}
10771076
let fieldName = (<StringLiteralExpression>firstOperand).value;
1078-
let classMembers = classReference.members;
1079-
if (classMembers && classMembers.has(fieldName)) {
1080-
let member = assert(classMembers.get(fieldName));
1081-
if (member.kind == ElementKind.Field) {
1082-
return contextualUsize(compiler, i64_new((<Field>member).memoryOffset), contextualType);
1077+
let fieldMember = classReference.getMember(fieldName);
1078+
if (fieldMember && fieldMember.kind == ElementKind.PropertyPrototype) {
1079+
let property = (<PropertyPrototype>fieldMember).instance;
1080+
if (property && property.isField) {
1081+
assert(property.memoryOffset >= 0);
1082+
return contextualUsize(compiler, i64_new(property.memoryOffset), contextualType);
10831083
}
10841084
}
10851085
compiler.error(
@@ -10261,32 +10261,31 @@ function ensureVisitMembersOf(compiler: Compiler, instance: Class): void {
1026110261
// TODO: for (let member of members.values()) {
1026210262
for (let _values = Map_values(members), j = 0, l = _values.length; j < l; ++j) {
1026310263
let member = unchecked(_values[j]);
10264-
if (member.kind == ElementKind.Field) {
10265-
if ((<Field>member).parent == instance) {
10266-
let fieldType = (<Field>member).type;
10267-
if (fieldType.isManaged) {
10268-
let fieldOffset = (<Field>member).memoryOffset;
10269-
assert(fieldOffset >= 0);
10270-
needsTempValue = true;
10271-
body.push(
10272-
// if ($2 = value) __visit($2, $1)
10273-
module.if(
10274-
module.local_tee(2,
10275-
module.load(sizeTypeSize, false,
10276-
module.local_get(0, sizeTypeRef),
10277-
sizeTypeRef, fieldOffset
10278-
),
10279-
false // internal
10280-
),
10281-
module.call(visitInstance.internalName, [
10282-
module.local_get(2, sizeTypeRef), // value
10283-
module.local_get(1, TypeRef.I32) // cookie
10284-
], TypeRef.None)
10285-
)
10286-
);
10287-
}
10288-
}
10289-
}
10264+
if (member.kind != ElementKind.PropertyPrototype) continue;
10265+
// Class should have resolved fields during finalization
10266+
let property = (<PropertyPrototype>member).instance;
10267+
if (!property) continue;
10268+
let fieldType = property.type;
10269+
if (!property.isField || property.getBoundClassOrInterface() != instance || !fieldType.isManaged) continue;
10270+
let fieldOffset = property.memoryOffset;
10271+
assert(fieldOffset >= 0);
10272+
needsTempValue = true;
10273+
body.push(
10274+
// if ($2 = value) __visit($2, $1)
10275+
module.if(
10276+
module.local_tee(2,
10277+
module.load(sizeTypeSize, false,
10278+
module.local_get(0, sizeTypeRef),
10279+
sizeTypeRef, fieldOffset
10280+
),
10281+
false // internal
10282+
),
10283+
module.call(visitInstance.internalName, [
10284+
module.local_get(2, sizeTypeRef), // value
10285+
module.local_get(1, TypeRef.I32) // cookie
10286+
], TypeRef.None)
10287+
)
10288+
);
1029010289
}
1029110290
}
1029210291
}
@@ -10458,66 +10457,6 @@ export function compileRTTI(compiler: Compiler): void {
1045810457
}
1045910458
}
1046010459

10461-
/** Compiles a class-specific instanceof helper, checking a ref against all concrete instances. */
10462-
export function compileClassInstanceOf(compiler: Compiler, prototype: ClassPrototype): void {
10463-
let module = compiler.module;
10464-
let sizeTypeRef = compiler.options.sizeTypeRef;
10465-
let instanceofInstance = assert(prototype.program.instanceofInstance);
10466-
compiler.compileFunction(instanceofInstance);
10467-
10468-
let stmts = new Array<ExpressionRef>();
10469-
10470-
// if (!ref) return false
10471-
stmts.push(
10472-
module.if(
10473-
module.unary(
10474-
sizeTypeRef == TypeRef.I64
10475-
? UnaryOp.EqzI64
10476-
: UnaryOp.EqzI32,
10477-
module.local_get(0, sizeTypeRef)
10478-
),
10479-
module.return(
10480-
module.i32(0)
10481-
)
10482-
)
10483-
);
10484-
10485-
// if (__instanceof(ref, ID[i])) return true
10486-
let instances = prototype.instances;
10487-
if (instances && instances.size > 0) {
10488-
// TODO: for (let instance of instances.values()) {
10489-
for (let _values = Map_values(instances), i = 0, k = _values.length; i < k; ++i) {
10490-
let instance = unchecked(_values[i]);
10491-
stmts.push(
10492-
module.if(
10493-
module.call(instanceofInstance.internalName, [
10494-
module.local_get(0, sizeTypeRef),
10495-
module.i32(instance.id)
10496-
], TypeRef.I32),
10497-
module.return(
10498-
module.i32(1)
10499-
)
10500-
)
10501-
);
10502-
}
10503-
}
10504-
10505-
// return false
10506-
stmts.push(
10507-
module.return(
10508-
module.i32(0)
10509-
)
10510-
);
10511-
10512-
module.addFunction(
10513-
`${prototype.internalName}~instanceof`,
10514-
sizeTypeRef,
10515-
TypeRef.I32,
10516-
null,
10517-
module.flatten(stmts)
10518-
);
10519-
}
10520-
1052110460
// Helpers
1052210461

1052310462
let checkConstantType_expr: ExpressionRef = 0;

Diff for: src/common.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ export const enum CommonFlags {
7373
Scoped = 1 << 26,
7474
/** Is a stub. */
7575
Stub = 1 << 27,
76-
/** Is a virtual method. */
77-
Virtual = 1 << 28,
76+
/** Is an overridden method. */
77+
Overridden = 1 << 28,
7878
/** Is (part of) a closure. */
7979
Closure = 1 << 29,
8080

@@ -257,7 +257,6 @@ export namespace CommonNames {
257257
export const link = "__link";
258258
export const collect = "__collect";
259259
export const typeinfo = "__typeinfo";
260-
export const instanceof_ = "__instanceof";
261260
export const visit = "__visit";
262261
export const newBuffer = "__newBuffer";
263262
export const newArray = "__newArray";

0 commit comments

Comments
 (0)