Skip to content

Commit 3b0fd9a

Browse files
committed
Implement isDefined and isConstant builtins
1 parent 10a9f40 commit 3b0fd9a

12 files changed

+278
-146
lines changed

dist/asc.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/asc.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/assemblyscript.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/assemblyscript.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/builtins.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ import {
5151
FlowFlags
5252
} from "./program";
5353

54+
import {
55+
ReportMode
56+
} from "./resolver";
57+
import { CommonFlags } from "./common";
58+
5459
/** Compiles a call to a built-in function. */
5560
export function compileCall(
5661
compiler: Compiler,
@@ -128,6 +133,57 @@ export function compileCall(
128133
? module.createI32(1)
129134
: module.createI32(0);
130135
}
136+
case "isDefined": { // isDefined(expression) -> bool
137+
compiler.currentType = Type.bool;
138+
if (operands.length != 1) {
139+
if (typeArguments) {
140+
compiler.error(
141+
DiagnosticCode.Type_0_is_not_generic,
142+
reportNode.range, prototype.internalName
143+
);
144+
}
145+
compiler.error(
146+
DiagnosticCode.Expected_0_arguments_but_got_1,
147+
reportNode.range, "1", operands.length.toString(10)
148+
);
149+
return module.createUnreachable();
150+
}
151+
if (typeArguments) {
152+
compiler.error(
153+
DiagnosticCode.Type_0_is_not_generic,
154+
reportNode.range, prototype.internalName
155+
);
156+
return module.createUnreachable();
157+
}
158+
let element = compiler.resolver.resolveExpression(operands[0], compiler.currentFunction, ReportMode.SWALLOW);
159+
return module.createI32(element ? 1 : 0);
160+
}
161+
case "isConstant": { // isConstant(expression) -> bool
162+
compiler.currentType = Type.bool;
163+
if (operands.length != 1) {
164+
if (typeArguments) {
165+
compiler.error(
166+
DiagnosticCode.Type_0_is_not_generic,
167+
reportNode.range, prototype.internalName
168+
);
169+
}
170+
compiler.error(
171+
DiagnosticCode.Expected_0_arguments_but_got_1,
172+
reportNode.range, "1", operands.length.toString(10)
173+
);
174+
return module.createUnreachable();
175+
}
176+
if (typeArguments) {
177+
compiler.error(
178+
DiagnosticCode.Type_0_is_not_generic,
179+
reportNode.range, prototype.internalName
180+
);
181+
return module.createUnreachable();
182+
}
183+
let expr = compiler.compileExpressionRetainType(operands[0], Type.i32, WrapMode.NONE);
184+
compiler.currentType = Type.bool;
185+
return module.createI32(getExpressionId(expr) == ExpressionId.Const ? 1 : 0);
186+
}
131187

132188
// math
133189

src/compiler.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4623,7 +4623,7 @@ export class Compiler extends DiagnosticEmitter {
46234623
return this.module.createUnreachable();
46244624
}
46254625
case ElementKind.CLASS: {
4626-
if (resolver.resolvedElementExpression) { // indexed access
4626+
if (resolver.currentElementExpression) { // indexed access
46274627
let isUnchecked = currentFunction.flow.is(FlowFlags.UNCHECKED_CONTEXT);
46284628
let indexedSet = (<Class>target).lookupOverload(OperatorKind.INDEXED_SET, isUnchecked);
46294629
if (!indexedSet) {
@@ -4736,7 +4736,7 @@ export class Compiler extends DiagnosticEmitter {
47364736
);
47374737
return module.createUnreachable();
47384738
}
4739-
let thisExpression = assert(this.resolver.resolvedThisExpression);
4739+
let thisExpression = assert(this.resolver.currentThisExpression);
47404740
let thisExpr = this.compileExpressionRetainType(
47414741
thisExpression,
47424742
this.options.usizeType,
@@ -4788,7 +4788,7 @@ export class Compiler extends DiagnosticEmitter {
47884788
// call just the setter if the return value isn't of interest
47894789
if (!tee) {
47904790
if (setterInstance.is(CommonFlags.INSTANCE)) {
4791-
let thisExpression = assert(this.resolver.resolvedThisExpression);
4791+
let thisExpression = assert(this.resolver.currentThisExpression);
47924792
let thisExpr = this.compileExpressionRetainType(
47934793
thisExpression,
47944794
this.options.usizeType,
@@ -4808,7 +4808,7 @@ export class Compiler extends DiagnosticEmitter {
48084808
let returnType = getterInstance.signature.returnType;
48094809
let nativeReturnType = returnType.toNativeType();
48104810
if (setterInstance.is(CommonFlags.INSTANCE)) {
4811-
let thisExpression = assert(this.resolver.resolvedThisExpression);
4811+
let thisExpression = assert(this.resolver.currentThisExpression);
48124812
let thisExpr = this.compileExpressionRetainType(
48134813
thisExpression,
48144814
this.options.usizeType,
@@ -4841,7 +4841,7 @@ export class Compiler extends DiagnosticEmitter {
48414841
return module.createUnreachable();
48424842
}
48434843
case ElementKind.CLASS: {
4844-
let elementExpression = this.resolver.resolvedElementExpression;
4844+
let elementExpression = this.resolver.currentElementExpression;
48454845
if (elementExpression) {
48464846
let isUnchecked = this.currentFunction.flow.is(FlowFlags.UNCHECKED_CONTEXT);
48474847
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET, isUnchecked);
@@ -4862,7 +4862,7 @@ export class Compiler extends DiagnosticEmitter {
48624862
return module.createUnreachable();
48634863
}
48644864
let targetType = (<Class>target).type;
4865-
let thisExpression = assert(this.resolver.resolvedThisExpression);
4865+
let thisExpression = assert(this.resolver.currentThisExpression);
48664866
let thisExpr = this.compileExpressionRetainType(
48674867
thisExpression,
48684868
this.options.usizeType,
@@ -5039,7 +5039,7 @@ export class Compiler extends DiagnosticEmitter {
50395039
let thisExpr: ExpressionRef = 0;
50405040
if (instance.is(CommonFlags.INSTANCE)) {
50415041
thisExpr = this.compileExpressionRetainType(
5042-
assert(this.resolver.resolvedThisExpression),
5042+
assert(this.resolver.currentThisExpression),
50435043
this.options.usizeType,
50445044
WrapMode.NONE
50455045
);
@@ -5082,7 +5082,7 @@ export class Compiler extends DiagnosticEmitter {
50825082
case ElementKind.FIELD: {
50835083
let type = (<Field>target).type;
50845084
if (signature = type.signatureReference) {
5085-
let thisExpression = assert(this.resolver.resolvedThisExpression);
5085+
let thisExpression = assert(this.resolver.currentThisExpression);
50865086
let thisExpr = this.compileExpressionRetainType(
50875087
thisExpression,
50885088
this.options.usizeType,
@@ -6598,7 +6598,7 @@ export class Compiler extends DiagnosticEmitter {
65986598
return module.createGetGlobal((<EnumValue>target).internalName, NativeType.I32);
65996599
}
66006600
case ElementKind.FIELD: { // instance field
6601-
let thisExpression = assert(this.resolver.resolvedThisExpression);
6601+
let thisExpression = assert(this.resolver.currentThisExpression);
66026602
assert((<Field>target).memoryOffset >= 0);
66036603
let thisExpr = this.compileExpressionRetainType(
66046604
thisExpression,
@@ -6632,7 +6632,7 @@ export class Compiler extends DiagnosticEmitter {
66326632
if (instance.is(CommonFlags.INSTANCE)) {
66336633
let parent = assert(instance.parent);
66346634
assert(parent.kind == ElementKind.CLASS);
6635-
let thisExpression = assert(this.resolver.resolvedThisExpression);
6635+
let thisExpression = assert(this.resolver.currentThisExpression);
66366636
let thisExpr = this.compileExpressionRetainType(
66376637
thisExpression,
66386638
this.options.usizeType,

src/resolver.ts

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ import {
6060
} from "./common";
6161

6262
/** Indicates whether errors are reported or not. */
63-
enum ReportMode {
63+
export enum ReportMode {
6464
/** Report errors. */
6565
REPORT,
6666
/** Swallow errors. */
@@ -72,10 +72,11 @@ export class Resolver extends DiagnosticEmitter {
7272

7373
/** The program this resolver belongs to. */
7474
program: Program;
75+
7576
/** Target expression of the previously resolved property or element access. */
76-
resolvedThisExpression: Expression | null = null;
77+
currentThisExpression: Expression | null = null;
7778
/** Element expression of the previously resolved element access. */
78-
resolvedElementExpression : Expression | null = null;
79+
currentElementExpression : Expression | null = null;
7980

8081
/** Constructs the resolver for the specified program. */
8182
constructor(program: Program) {
@@ -306,8 +307,8 @@ export class Resolver extends DiagnosticEmitter {
306307
case ElementKind.FUNCTION: { // search locals, use prototype
307308
element = (<Function>context).flow.getScopedLocal(name);
308309
if (element) {
309-
this.resolvedThisExpression = null;
310-
this.resolvedElementExpression = null;
310+
this.currentThisExpression = null;
311+
this.currentElementExpression = null;
311312
return element;
312313
}
313314
context = (<Function>context).prototype.parent;
@@ -324,8 +325,8 @@ export class Resolver extends DiagnosticEmitter {
324325
let members = context.members;
325326
if (members) {
326327
if (element = members.get(name)) {
327-
this.resolvedThisExpression = null;
328-
this.resolvedElementExpression = null;
328+
this.currentThisExpression = null;
329+
this.currentElementExpression = null;
329330
return element;
330331
}
331332
}
@@ -336,15 +337,15 @@ export class Resolver extends DiagnosticEmitter {
336337
// search current file
337338
var elementsLookup = this.program.elementsLookup;
338339
if (element = elementsLookup.get(identifier.range.source.internalPath + PATH_DELIMITER + name)) {
339-
this.resolvedThisExpression = null;
340-
this.resolvedElementExpression = null;
340+
this.currentThisExpression = null;
341+
this.currentElementExpression = null;
341342
return element; // GLOBAL, FUNCTION_PROTOTYPE, CLASS_PROTOTYPE
342343
}
343344

344345
// search global scope
345346
if (element = elementsLookup.get(name)) {
346-
this.resolvedThisExpression = null;
347-
this.resolvedElementExpression = null;
347+
this.currentThisExpression = null;
348+
this.currentElementExpression = null;
348349
return element; // GLOBAL, FUNCTION_PROTOTYPE, CLASS_PROTOTYPE
349350
}
350351

@@ -407,7 +408,7 @@ export class Resolver extends DiagnosticEmitter {
407408
break;
408409
}
409410
case ElementKind.CLASS: {
410-
let elementExpression = this.resolvedElementExpression;
411+
let elementExpression = this.currentElementExpression;
411412
if (elementExpression) {
412413
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
413414
if (!indexedGet) {
@@ -438,8 +439,8 @@ export class Resolver extends DiagnosticEmitter {
438439
let members = target.members;
439440
let member: Element | null;
440441
if (members && (member = members.get(propertyName))) {
441-
this.resolvedThisExpression = targetExpression;
442-
this.resolvedElementExpression = null;
442+
this.currentThisExpression = targetExpression;
443+
this.currentElementExpression = null;
443444
return member; // instance FIELD, static GLOBAL, FUNCTION_PROTOTYPE...
444445
}
445446
// traverse inherited static members on the base prototype if target is a class prototype
@@ -467,8 +468,8 @@ export class Resolver extends DiagnosticEmitter {
467468
if (members) {
468469
let member = members.get(propertyName);
469470
if (member) {
470-
this.resolvedThisExpression = targetExpression;
471-
this.resolvedElementExpression = null;
471+
this.currentThisExpression = targetExpression;
472+
this.currentElementExpression = null;
472473
return member; // static ENUMVALUE, static GLOBAL, static FUNCTION_PROTOTYPE...
473474
}
474475
}
@@ -496,8 +497,8 @@ export class Resolver extends DiagnosticEmitter {
496497
case ElementKind.FIELD: {
497498
let type = (<VariableLikeElement>target).type;
498499
if (target = type.classReference) {
499-
this.resolvedThisExpression = targetExpression;
500-
this.resolvedElementExpression = elementAccess.elementExpression;
500+
this.currentThisExpression = targetExpression;
501+
this.currentElementExpression = elementAccess.elementExpression;
501502
return target;
502503
}
503504
break;
@@ -515,8 +516,8 @@ export class Resolver extends DiagnosticEmitter {
515516
}
516517
let returnType = indexedGet.signature.returnType;
517518
if (target = returnType.classReference) {
518-
this.resolvedThisExpression = targetExpression;
519-
this.resolvedElementExpression = elementAccess.elementExpression;
519+
this.currentThisExpression = targetExpression;
520+
this.currentElementExpression = elementAccess.elementExpression;
520521
return target;
521522
}
522523
break;
@@ -549,8 +550,8 @@ export class Resolver extends DiagnosticEmitter {
549550
if (type) {
550551
let classType = type.classReference;
551552
if (classType) {
552-
this.resolvedThisExpression = null;
553-
this.resolvedElementExpression = null;
553+
this.currentThisExpression = null;
554+
this.currentElementExpression = null;
554555
return classType;
555556
}
556557
}
@@ -563,15 +564,15 @@ export class Resolver extends DiagnosticEmitter {
563564
if (contextualFunction.flow.is(FlowFlags.INLINE_CONTEXT)) {
564565
let explicitLocal = contextualFunction.flow.getScopedLocal("this");
565566
if (explicitLocal) {
566-
this.resolvedThisExpression = null;
567-
this.resolvedElementExpression = null;
567+
this.currentThisExpression = null;
568+
this.currentElementExpression = null;
568569
return explicitLocal;
569570
}
570571
}
571572
let parent = contextualFunction.parent;
572573
if (parent) {
573-
this.resolvedThisExpression = null;
574-
this.resolvedElementExpression = null;
574+
this.currentThisExpression = null;
575+
this.currentElementExpression = null;
575576
return parent;
576577
}
577578
if (reportMode == ReportMode.REPORT) {
@@ -586,15 +587,15 @@ export class Resolver extends DiagnosticEmitter {
586587
if (contextualFunction.flow.is(FlowFlags.INLINE_CONTEXT)) {
587588
let explicitLocal = contextualFunction.flow.getScopedLocal("super");
588589
if (explicitLocal) {
589-
this.resolvedThisExpression = null;
590-
this.resolvedElementExpression = null;
590+
this.currentThisExpression = null;
591+
this.currentElementExpression = null;
591592
return explicitLocal;
592593
}
593594
}
594595
let parent = contextualFunction.parent;
595596
if (parent && parent.kind == ElementKind.CLASS && (parent = (<Class>parent).base)) {
596-
this.resolvedThisExpression = null;
597-
this.resolvedElementExpression = null;
597+
this.currentThisExpression = null;
598+
this.currentElementExpression = null;
598599
return parent;
599600
}
600601
if (reportMode == ReportMode.REPORT) {
@@ -611,8 +612,8 @@ export class Resolver extends DiagnosticEmitter {
611612
case NodeKind.LITERAL: {
612613
switch ((<LiteralExpression>expression).literalKind) {
613614
case LiteralKind.STRING: {
614-
this.resolvedThisExpression = expression;
615-
this.resolvedElementExpression = null;
615+
this.currentThisExpression = expression;
616+
this.currentElementExpression = null;
616617
return this.program.stringInstance;
617618
}
618619
// case LiteralKind.ARRAY: // TODO

std/assembly/builtins.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export declare function isString<T>(value?: T): bool;
1010

1111
export declare function isArray<T>(value?: T): bool;
1212

13+
export declare function isDefined(expression: void): bool;
14+
15+
export declare function isConstant(expression: void): bool;
16+
1317
export const NaN: f64 = 0 / 0;
1418

1519
export const Infinity: f64 = 1 / 0;

std/assembly/index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,10 @@ declare function isReference<T>(value?: any): value is object | string;
304304
declare function isString<T>(value?: any): value is string | String;
305305
/** Tests if the specified type *or* expression can be used as an array. Compiles to a constant. */
306306
declare function isArray<T>(value?: any): value is Array<any>;
307+
/** Tests if the specified expression resolves to a defined element. */
308+
declare function isDefined(expression: any): bool;
309+
/** Tests if the specified expression evaluates to a constant value. */
310+
declare function isConstant(expression: any): bool;
307311
/** Traps if the specified value is not true-ish, otherwise returns the (non-nullable) value. */
308312
declare function assert<T>(isTrueish: T, message?: string): T & object; // any better way to model `: T != null`?
309313
/** Parses an integer string to a 64-bit float. */

0 commit comments

Comments
 (0)