Skip to content

Commit e68fbb4

Browse files
authored
Refactor static properties / use correct this arguments (AssemblyScript#1259)
1 parent 1868f2e commit e68fbb4

13 files changed

+422
-307
lines changed

cli/asc.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,6 @@
243243
"category": "Binaryen",
244244
"description": "Skips validating the module using Binaryen.",
245245
"type": "b",
246-
"alias": "c",
247246
"default": false
248247
},
249248

src/compiler.ts

Lines changed: 174 additions & 184 deletions
Large diffs are not rendered by default.

src/definitions.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,10 @@ export abstract class ExportsWalker {
122122
break;
123123
}
124124
case ElementKind.PROPERTY_PROTOTYPE: {
125-
this.visitPropertyInstances(name, <PropertyPrototype>element);
125+
let propertyInstance = (<PropertyPrototype>element).instance;
126+
if (!propertyInstance) break;
127+
element = propertyInstance;
128+
// fall-through
126129
break;
127130
}
128131
case ElementKind.PROPERTY: {
@@ -164,16 +167,6 @@ export abstract class ExportsWalker {
164167
}
165168
}
166169

167-
private visitPropertyInstances(name: string, element: PropertyPrototype): void {
168-
// var instances = element.instances;
169-
// if (instances) {
170-
// for (let instance of instances.values()) {
171-
// if (instance.is(CommonFlags.COMPILED)) this.visitProperty(instance);
172-
// }
173-
// }
174-
assert(false);
175-
}
176-
177170
abstract visitGlobal(name: string, element: Global): void;
178171
abstract visitEnum(name: string, element: Enum): void;
179172
abstract visitFunction(name: string, element: Function): void;

src/flow.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -412,9 +412,10 @@ export class Flow {
412412

413413
/** Adds a new scoped alias for the specified local. For example `super` aliased to the `this` local. */
414414
addScopedAlias(name: string, type: Type, index: i32, reportNode: Node | null = null): Local {
415-
if (!this.scopedLocals) this.scopedLocals = new Map();
415+
var scopedLocals = this.scopedLocals;
416+
if (!scopedLocals) this.scopedLocals = scopedLocals = new Map();
416417
else {
417-
let existingLocal = this.scopedLocals.get(name);
418+
let existingLocal = scopedLocals.get(name);
418419
if (existingLocal) {
419420
if (reportNode) {
420421
if (!existingLocal.declaration.range.source.isNative) {
@@ -437,7 +438,7 @@ export class Flow {
437438
assert(index < this.parentFunction.localsByIndex.length);
438439
var scopedAlias = new Local(name, index, type, this.parentFunction);
439440
// not flagged as SCOPED as it must not be free'd when the flow is finalized
440-
this.scopedLocals.set(name, scopedAlias);
441+
scopedLocals.set(name, scopedAlias);
441442
return scopedAlias;
442443
}
443444

src/program.ts

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3210,7 +3210,10 @@ export class FunctionPrototype extends DeclaredElement {
32103210
get isBound(): bool {
32113211
var parent = this.parent;
32123212
return parent.kind == ElementKind.CLASS
3213-
|| parent.kind == ElementKind.PROPERTY_PROTOTYPE && parent.parent.kind == ElementKind.CLASS;
3213+
|| parent.kind == ElementKind.PROPERTY_PROTOTYPE && (
3214+
parent.parent.kind == ElementKind.CLASS ||
3215+
parent.parent.kind == ElementKind.INTERFACE
3216+
);
32143217
}
32153218

32163219
/** Creates a clone of this prototype that is bound to a concrete class instead. */
@@ -3415,8 +3418,9 @@ export class Function extends TypedElement {
34153418
/** Finalizes the function once compiled, releasing no longer needed resources. */
34163419
finalize(module: Module, ref: FunctionRef): void {
34173420
this.ref = ref;
3418-
assert(!this.breakStack || !this.breakStack.length); // internal error
3419-
this.breakStack = null;
3421+
var breakStack = this.breakStack;
3422+
assert(!breakStack || !breakStack.length); // internal error
3423+
this.breakStack = breakStack = null;
34203424
this.breakLabel = null;
34213425
this.tempI32s = this.tempI64s = this.tempF32s = this.tempF64s = null;
34223426
if (this.program.options.sourceMap) {
@@ -3571,13 +3575,18 @@ export class PropertyPrototype extends DeclaredElement {
35713575
getterPrototype: FunctionPrototype | null = null;
35723576
/** Setter prototype. */
35733577
setterPrototype: FunctionPrototype | null = null;
3578+
/** Property instance, if resolved. */
3579+
instance: Property | null = null;
3580+
3581+
/** Clones of this prototype that are bound to specific classes. */
3582+
private boundPrototypes: Map<Class,PropertyPrototype> | null = null;
35743583

35753584
/** Constructs a new property prototype. */
35763585
constructor(
35773586
/** Simple name. */
35783587
name: string,
3579-
/** Parent class. */
3580-
parent: ClassPrototype,
3588+
/** Parent element. Either a class prototype or instance. */
3589+
parent: Element,
35813590
/** Declaration of the getter or setter introducing the property. */
35823591
firstDeclaration: FunctionDeclaration
35833592
) {
@@ -3596,6 +3605,42 @@ export class PropertyPrototype extends DeclaredElement {
35963605
lookup(name: string): Element | null {
35973606
return this.parent.lookup(name);
35983607
}
3608+
3609+
/** Tests if this prototype is bound to a class. */
3610+
get isBound(): bool {
3611+
switch (this.parent.kind) {
3612+
case ElementKind.CLASS:
3613+
case ElementKind.INTERFACE: return true;
3614+
}
3615+
return false;
3616+
}
3617+
3618+
/** Creates a clone of this prototype that is bound to a concrete class instead. */
3619+
toBound(classInstance: Class): PropertyPrototype {
3620+
assert(this.is(CommonFlags.INSTANCE));
3621+
assert(!this.isBound);
3622+
var boundPrototypes = this.boundPrototypes;
3623+
if (!boundPrototypes) this.boundPrototypes = boundPrototypes = new Map();
3624+
else if (boundPrototypes.has(classInstance)) return assert(boundPrototypes.get(classInstance));
3625+
var firstDeclaration = this.declaration;
3626+
assert(firstDeclaration.kind == NodeKind.METHODDECLARATION);
3627+
var bound = new PropertyPrototype(
3628+
this.name,
3629+
classInstance, // !
3630+
<MethodDeclaration>firstDeclaration
3631+
);
3632+
bound.flags = this.flags;
3633+
var getterPrototype = this.getterPrototype;
3634+
if (getterPrototype) {
3635+
bound.getterPrototype = getterPrototype.toBound(classInstance);
3636+
}
3637+
var setterPrototype = this.setterPrototype;
3638+
if (setterPrototype) {
3639+
bound.setterPrototype = setterPrototype.toBound(classInstance);
3640+
}
3641+
boundPrototypes.set(classInstance, bound);
3642+
return bound;
3643+
}
35993644
}
36003645

36013646
/** A resolved property. */
@@ -3631,7 +3676,9 @@ export class Property extends VariableLikeElement {
36313676
this.prototype = prototype;
36323677
this.flags = prototype.flags;
36333678
this.decoratorFlags = prototype.decoratorFlags;
3634-
registerConcreteElement(this.program, this);
3679+
if (this.is(CommonFlags.INSTANCE)) {
3680+
registerConcreteElement(this.program, this);
3681+
}
36353682
}
36363683

36373684
/* @override */
@@ -3860,8 +3907,8 @@ export class Class extends TypedElement {
38603907
return lengthField !== null && (
38613908
lengthField.kind == ElementKind.FIELD ||
38623909
(
3863-
lengthField.kind == ElementKind.PROPERTY &&
3864-
(<Property>lengthField).getterInstance !== null // TODO: resolve & check type?
3910+
lengthField.kind == ElementKind.PROPERTY_PROTOTYPE &&
3911+
(<PropertyPrototype>lengthField).getterPrototype !== null // TODO: resolve & check type?
38653912
)
38663913
) && (
38673914
this.lookupOverload(OperatorKind.INDEXED_GET) !== null ||
@@ -3908,9 +3955,10 @@ export class Class extends TypedElement {
39083955
throw new Error("type argument count mismatch");
39093956
}
39103957
if (numTypeArguments) {
3911-
if (!this.contextualTypeArguments) this.contextualTypeArguments = new Map();
3958+
let contextualTypeArguments = this.contextualTypeArguments;
3959+
if (!contextualTypeArguments) this.contextualTypeArguments = contextualTypeArguments = new Map();
39123960
for (let i = 0; i < numTypeArguments; ++i) {
3913-
this.contextualTypeArguments.set(typeParameters[i].name.text, typeArguments[i]);
3961+
contextualTypeArguments.set(typeParameters[i].name.text, typeArguments[i]);
39143962
}
39153963
}
39163964
} else if (typeParameters !== null && typeParameters.length > 0) {
@@ -4402,8 +4450,8 @@ export function mangleInternalName(name: string, parent: Element, isInstance: bo
44024450
assert(!isInstance);
44034451
return parent.internalName + INNER_DELIMITER + name;
44044452
}
4405-
case ElementKind.PROPERTY_PROTOTYPE:
4406-
case ElementKind.PROPERTY: {
4453+
case ElementKind.PROPERTY_PROTOTYPE: // properties are just containers
4454+
case ElementKind.PROPERTY: { //
44074455
parent = parent.parent;
44084456
// fall-through
44094457
}

src/resolver.ts

Lines changed: 62 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,32 +1313,10 @@ export class Resolver extends DiagnosticEmitter {
13131313
break;
13141314
}
13151315
case ElementKind.PROPERTY_PROTOTYPE: { // SomeClass.prop
1316-
let propertyPrototype = <PropertyPrototype>target;
1317-
let getterInstance = this.resolveFunction( // reports
1318-
assert(propertyPrototype.getterPrototype), // must have a getter
1319-
null,
1320-
makeMap<string,Type>(),
1321-
reportMode
1322-
);
1323-
if (!getterInstance) return null;
1324-
let type = getterInstance.signature.returnType;
1325-
let classReference = type.classReference;
1326-
if (!classReference) {
1327-
let wrapperClasses = this.program.wrapperClasses;
1328-
if (wrapperClasses.has(type)) {
1329-
classReference = assert(wrapperClasses.get(type));
1330-
} else {
1331-
if (reportMode == ReportMode.REPORT) {
1332-
this.error(
1333-
DiagnosticCode.Property_0_does_not_exist_on_type_1,
1334-
node.property.range, propertyName, type.toString()
1335-
);
1336-
}
1337-
return null;
1338-
}
1339-
}
1340-
target = classReference;
1341-
break;
1316+
let propertyInstance = this.resolveProperty(<PropertyPrototype>target, reportMode);
1317+
if (!propertyInstance) return null;
1318+
target = propertyInstance;
1319+
// fall-through
13421320
}
13431321
case ElementKind.PROPERTY: { // someInstance.prop
13441322
let propertyInstance = <Property>target;
@@ -1420,9 +1398,21 @@ export class Resolver extends DiagnosticEmitter {
14201398
do {
14211399
let members = target.members;
14221400
if (members !== null && members.has(propertyName)) {
1423-
this.currentThisExpression = targetNode;
1401+
let member = assert(members.get(propertyName));
1402+
if (member.kind == ElementKind.PROPERTY_PROTOTYPE) {
1403+
let propertyInstance = this.resolveProperty(<PropertyPrototype>member, reportMode);
1404+
if (!propertyInstance) return null;
1405+
member = propertyInstance;
1406+
if (propertyInstance.is(CommonFlags.STATIC)) {
1407+
this.currentThisExpression = null;
1408+
} else {
1409+
this.currentThisExpression = targetNode;
1410+
}
1411+
} else {
1412+
this.currentThisExpression = targetNode;
1413+
}
14241414
this.currentElementExpression = null;
1425-
return assert(members.get(propertyName)); // instance FIELD, static GLOBAL, FUNCTION_PROTOTYPE...
1415+
return member; // instance FIELD, static GLOBAL, FUNCTION_PROTOTYPE, PROPERTY...
14261416
}
14271417
// traverse inherited static members on the base prototype if target is a class prototype
14281418
if (
@@ -3129,39 +3119,8 @@ export class Resolver extends DiagnosticEmitter {
31293119
break;
31303120
}
31313121
case ElementKind.PROPERTY_PROTOTYPE: {
3132-
let propertyPrototype = <PropertyPrototype>member;
3133-
let propertyInstance = new Property(propertyPrototype, instance);
3134-
properties.push(propertyInstance);
3135-
let getterPrototype = propertyPrototype.getterPrototype;
3136-
if (getterPrototype) {
3137-
let getterInstance = this.resolveFunction(
3138-
getterPrototype.toBound(instance),
3139-
null,
3140-
makeMap(instance.contextualTypeArguments),
3141-
reportMode
3142-
);
3143-
if (getterInstance) {
3144-
propertyInstance.getterInstance = getterInstance;
3145-
propertyInstance.setType(getterInstance.signature.returnType);
3146-
}
3147-
}
3148-
let setterPrototype = propertyPrototype.setterPrototype;
3149-
if (setterPrototype) {
3150-
let setterInstance = this.resolveFunction(
3151-
setterPrototype.toBound(instance),
3152-
null,
3153-
makeMap(instance.contextualTypeArguments),
3154-
reportMode
3155-
);
3156-
if (setterInstance) {
3157-
propertyInstance.setterInstance = setterInstance;
3158-
if (!propertyInstance.is(CommonFlags.RESOLVED)) {
3159-
assert(setterInstance.signature.parameterTypes.length == 1);
3160-
propertyInstance.setType(setterInstance.signature.parameterTypes[0]);
3161-
}
3162-
}
3163-
}
3164-
instance.add(propertyInstance.name, propertyInstance); // reports
3122+
let boundPrototype = (<PropertyPrototype>member).toBound(instance);
3123+
instance.add(boundPrototype.name, boundPrototype); // reports
31653124
break;
31663125
}
31673126
default: assert(false);
@@ -3368,4 +3327,46 @@ export class Resolver extends DiagnosticEmitter {
33683327
reportMode
33693328
);
33703329
}
3330+
3331+
/** Resolves a property prototype. */
3332+
resolveProperty(
3333+
/** The prototype of the property. */
3334+
prototype: PropertyPrototype,
3335+
/** How to proceed with eventual diagnostics. */
3336+
reportMode: ReportMode = ReportMode.REPORT
3337+
): Property | null {
3338+
var instance = prototype.instance;
3339+
if (instance) return instance;
3340+
prototype.instance = instance = new Property(prototype, prototype);
3341+
var getterPrototype = prototype.getterPrototype;
3342+
if (getterPrototype) {
3343+
let getterInstance = this.resolveFunction(
3344+
getterPrototype,
3345+
null,
3346+
makeMap<string,Type>(),
3347+
reportMode
3348+
);
3349+
if (getterInstance) {
3350+
instance.getterInstance = getterInstance;
3351+
instance.setType(getterInstance.signature.returnType);
3352+
}
3353+
}
3354+
var setterPrototype = prototype.setterPrototype;
3355+
if (setterPrototype) {
3356+
let setterInstance = this.resolveFunction(
3357+
setterPrototype,
3358+
null,
3359+
makeMap<string,Type>(),
3360+
reportMode
3361+
);
3362+
if (setterInstance) {
3363+
instance.setterInstance = setterInstance;
3364+
if (!instance.is(CommonFlags.RESOLVED)) {
3365+
assert(setterInstance.signature.parameterTypes.length == 1);
3366+
instance.setType(setterInstance.signature.parameterTypes[0]);
3367+
}
3368+
}
3369+
}
3370+
return instance;
3371+
}
33713372
}

tests/compiler/builtins.optimized.wat

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -592,9 +592,9 @@
592592
i32.const 5
593593
f64.const 0
594594
f64.const 0
595-
f64.const 29
596-
f64.const 30
597-
f64.const 30
595+
f64.const 23
596+
f64.const 24
597+
f64.const 24
598598
call $~lib/builtins/trace
599599
i32.const 1216
600600
i32.const 1216

tests/compiler/builtins.untouched.wat

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,11 +1727,11 @@
17271727
local.set $0
17281728
i32.const 0
17291729
local.set $1
1730-
i32.const 29
1730+
i32.const 23
17311731
local.set $6
1732-
i32.const 30
1732+
i32.const 24
17331733
local.set $7
1734-
i32.const 30
1734+
i32.const 24
17351735
local.set $8
17361736
i32.const 128
17371737
i32.const 5
@@ -1771,7 +1771,7 @@
17711771
unreachable
17721772
end
17731773
local.get $6
1774-
i32.const 29
1774+
i32.const 23
17751775
i32.eq
17761776
i32.eqz
17771777
if

0 commit comments

Comments
 (0)