Skip to content

Commit ef85993

Browse files
committed
Better testing infrastructure; Initial exports/imports/re-exports
1 parent 00303fd commit ef85993

34 files changed

+712
-247
lines changed

src/compiler.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,8 @@ export class Compiler extends DiagnosticEmitter {
257257
break;
258258

259259
case NodeKind.EXPORT:
260-
if ((<ExportStatement>statement).path)
261-
this.compileSourceByPath((<StringLiteralExpression>(<ExportStatement>statement).path).value, <StringLiteralExpression>(<ExportStatement>statement).path);
260+
if ((<ExportStatement>statement).normalizedPath != null)
261+
this.compileSourceByPath(<string>(<ExportStatement>statement).normalizedPath, <StringLiteralExpression>(<ExportStatement>statement).path);
262262
if (noTreeShaking || isEntry)
263263
this.compileExportStatement(<ExportStatement>statement);
264264
break;
@@ -517,10 +517,10 @@ export class Compiler extends DiagnosticEmitter {
517517
const internalPath: string | null = statement.path ? statement.internalPath : statement.range.source.internalPath;
518518
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i) {
519519
const member: ExportMember = members[i];
520-
const internalExportName: string = internalPath + PATH_DELIMITER + member.identifier.name;
520+
const internalExportName: string = internalPath + PATH_DELIMITER + member.externalIdentifier.name;
521521
const element: Element | null = <Element | null>this.program.exports.get(internalExportName);
522-
if (!element)
523-
throw new Error("unexpected missing element");
522+
if (!element) // reported in Program#initialize
523+
continue;
524524
switch (element.kind) {
525525

526526
case ElementKind.CLASS_PROTOTYPE:
@@ -1356,6 +1356,7 @@ export class Compiler extends DiagnosticEmitter {
13561356
// TODO: sizeof, load, store, see program.ts/initializeBuiltins
13571357
}
13581358
} else {
1359+
// TODO: infer type arguments from parameter types if omitted
13591360
functionInstance = (<FunctionPrototype>element).resolveInclTypeArguments(expression.typeArguments, this.currentFunction.contextualTypeArguments, expression); // reports
13601361
}
13611362
if (!functionInstance)

src/diagnosticMessages.generated.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export enum DiagnosticCode {
4848
_abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration = 1242,
4949
Duplicate_identifier_0 = 2300,
5050
Cannot_find_name_0 = 2304,
51+
Module_0_has_no_exported_member_1 = 2305,
5152
Generic_type_0_requires_1_type_argument_s = 2314,
5253
Type_0_is_not_generic = 2315,
5354
Type_0_is_not_assignable_to_type_1 = 2322,
@@ -115,6 +116,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
115116
case 1242: return "'abstract' modifier can only appear on a class, method, or property declaration.";
116117
case 2300: return "Duplicate identifier '{0}'.";
117118
case 2304: return "Cannot find name '{0}'.";
119+
case 2305: return "Module '{0}' has no exported member '{1}'.";
118120
case 2314: return "Generic type '{0}' requires {1} type argument(s).";
119121
case 2315: return "Type '{0}' is not generic.";
120122
case 2322: return "Type '{0}' is not assignable to type '{1}'.";

src/diagnosticMessages.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848

4949
"Duplicate identifier '{0}'.": 2300,
5050
"Cannot find name '{0}'.": 2304,
51+
"Module '{0}' has no exported member '{1}'.": 2305,
5152
"Generic type '{0}' requires {1} type argument(s).": 2314,
5253
"Type '{0}' is not generic.": 2315,
5354
"Type '{0}' is not assignable to type '{1}'.": 2322,

src/diagnostics.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ export abstract class DiagnosticEmitter {
154154
emitDiagnostic(code: DiagnosticCode, category: DiagnosticCategory, range: Range, arg0: string | null = null, arg1: string | null = null) {
155155
const message: DiagnosticMessage = DiagnosticMessage.create(code, category, arg0, arg1).withRange(range);
156156
this.diagnostics.push(message);
157-
console.log(formatDiagnosticMessage(message, true, true)); // temporary
158-
console.log(new Error().stack);
157+
console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
158+
// console.log(new Error().stack);
159159
}
160160

161161
error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {

src/module.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,14 @@ export class Module {
613613
return _BinaryenModuleValidate(this.ref) == 1;
614614
}
615615

616+
toBinary(): Uint8Array {
617+
throw new Error("not implemented");
618+
}
619+
620+
toText(): string {
621+
throw new Error("not implemented");
622+
}
623+
616624
dispose(): void {
617625
if (!this.ref) return; // sic
618626
_BinaryenModuleDispose(this.ref);

src/program.ts

Lines changed: 161 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Target } from "./compiler";
22
import { GETTER_PREFIX, SETTER_PREFIX, PATH_DELIMITER } from "./constants";
3-
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
3+
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter, DiagnosticCategory } from "./diagnostics";
44
import { Type, typesToString } from "./types";
55
import { I64 } from "./util";
66
import {
@@ -40,19 +40,20 @@ import {
4040
VariableDeclaration,
4141
VariableStatement,
4242

43-
hasModifier
43+
hasModifier,
44+
mangleInternalName
4445

4546
} from "./ast";
4647

4748
class QueuedExport {
48-
isForeign: bool;
49+
isReExport: bool;
4950
referencedName: string;
5051
member: ExportMember;
5152
}
5253

5354
class QueuedImport {
5455
internalName: string;
55-
importName: string;
56+
referencedName: string;
5657
declaration: ImportDeclaration;
5758
}
5859

@@ -130,61 +131,63 @@ export class Program extends DiagnosticEmitter {
130131
}
131132
}
132133

133-
// at this point queued exports should be resolvable
134-
for (let [exportName, queuedExport] of queuedExports) { // all file-level exports
135-
if (queuedExport.isForeign) {
136-
const seen: Set<QueuedExport> = new Set();
137-
while (queuedExports.has(queuedExport.referencedName)) {
138-
queuedExport = <QueuedExport>queuedExports.get(queuedExport.referencedName);
139-
if (seen.has(queuedExport))
140-
break;
141-
seen.add(queuedExport);
142-
}
143-
if (this.exports.has(queuedExport.referencedName)) {
144-
const element: Element = <Element>this.exports.get(queuedExport.referencedName);
145-
if (!this.exports.has(exportName))
146-
this.exports.set(exportName, element);
147-
if (queuedExport.member.range.source.isEntry)
148-
element.globalExportName = queuedExport.member.externalIdentifier.name;
149-
} else
150-
this.error(DiagnosticCode.Cannot_find_name_0, queuedExport.member.externalIdentifier.range, queuedExport.referencedName);
151-
} else /* local */ {
152-
if (this.elements.has(queuedExport.referencedName)) {
153-
const element: Element = <Element>this.elements.get(queuedExport.referencedName);
154-
if (!this.exports.has(exportName))
155-
this.exports.set(exportName, element);
156-
if (queuedExport.member.range.source.isEntry)
157-
element.globalExportName = queuedExport.member.externalIdentifier.name;
158-
} else
159-
this.error(DiagnosticCode.Cannot_find_name_0, queuedExport.member.externalIdentifier.range, queuedExport.referencedName);
134+
let element: Element | null;
135+
136+
// queued imports should be resolvable now
137+
for (let i: i32 = 0; i < queuedImports.length;) {
138+
const queuedImport: QueuedImport = queuedImports[i];
139+
element = this.tryResolveImport(queuedImport.referencedName, queuedExports);
140+
if (element) {
141+
this.elements.set(queuedImport.internalName, element);
142+
queuedImports.splice(i, 1);
143+
} else {
144+
this.error(DiagnosticCode.Module_0_has_no_exported_member_1, queuedImport.declaration.range, (<ImportStatement>queuedImport.declaration.parent).path.value, queuedImport.declaration.externalIdentifier.name);
145+
++i;
160146
}
161147
}
162148

163-
// at this point queued imports should be resolvable as well
164-
for (let i: i32 = 0, k: i32 = queuedImports.length; i < k; ++i) {
165-
const queuedImport: QueuedImport = queuedImports[i];
166-
const internalName: string = queuedImport.internalName;
167-
const seen: Set<QueuedExport> = new Set();
168-
let importName: string = queuedImport.importName;
169-
while (queuedExports.has(importName)) {
170-
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(importName);
171-
importName = queuedExport.referencedName;
172-
if (seen.has(queuedExport))
149+
// queued exports should be resolvable noww
150+
for (let [exportName, queuedExport] of queuedExports) {
151+
let currentExport: QueuedExport | null = queuedExport;
152+
do {
153+
if (currentExport.isReExport) {
154+
element = <Element | null>this.exports.get(currentExport.referencedName);
155+
if (element) {
156+
this.exports.set(exportName, element);
157+
break;
158+
}
159+
currentExport = <QueuedExport | null>queuedExports.get(currentExport.referencedName);
160+
if (!currentExport)
161+
this.error(DiagnosticCode.Module_0_has_no_exported_member_1, queuedExport.member.externalIdentifier.range, (<StringLiteralExpression>(<ExportStatement>queuedExport.member.parent).path).value, queuedExport.member.externalIdentifier.name);
162+
} else {
163+
element = <Element | null>this.elements.get(currentExport.referencedName);
164+
if (element)
165+
this.exports.set(exportName, element);
166+
else
167+
this.error(DiagnosticCode.Cannot_find_name_0, queuedExport.member.range, queuedExport.member.identifier.name);
173168
break;
174-
seen.add(queuedExport);
175-
}
176-
if (this.exports.has(importName)) {
177-
if (this.elements.has(internalName))
178-
this.error(DiagnosticCode.Duplicate_identifier_0, queuedImport.declaration.identifier.range, internalName);
179-
else {
180-
const element: Element = <Element>this.exports.get(importName);
181-
this.elements.set(internalName, element);
182169
}
183-
} else
184-
this.error(DiagnosticCode.Cannot_find_name_0, queuedImport.declaration.externalIdentifier.range, importName);
170+
} while (currentExport);
185171
}
186172
}
187173

174+
private tryResolveImport(referencedName: string, queuedExports: Map<string,QueuedExport>): Element | null {
175+
let element: Element | null;
176+
do {
177+
element = <Element | null>this.exports.get(referencedName);
178+
if (element)
179+
return element;
180+
const queuedExport: QueuedExport | null = <QueuedExport | null>queuedExports.get(referencedName);
181+
if (!queuedExport)
182+
return null;
183+
if (queuedExport.isReExport) {
184+
referencedName = queuedExport.referencedName;
185+
continue;
186+
}
187+
return <Element | null>this.elements.get(queuedExport.referencedName);
188+
} while (true);
189+
}
190+
188191
private initializeClass(declaration: ClassDeclaration): void {
189192
const internalName: string = declaration.internalName;
190193
if (this.elements.has(internalName)) {
@@ -288,21 +291,79 @@ export class Program extends DiagnosticEmitter {
288291
}
289292

290293
private initializeExport(member: ExportMember, internalPath: string | null, queuedExports: Map<string,QueuedExport>): void {
291-
const exportName: string = member.range.source.internalPath + PATH_DELIMITER + member.externalIdentifier.name;
292-
if (queuedExports.has(exportName)) {
293-
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, member.externalIdentifier.range, exportName);
294+
const externalName: string = member.range.source.internalPath + PATH_DELIMITER + member.externalIdentifier.name;
295+
296+
if (this.exports.has(externalName)) {
297+
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, member.externalIdentifier.range, externalName);
294298
return;
295299
}
296-
const queuedExport: QueuedExport = new QueuedExport();
300+
301+
let referencedName: string;
302+
303+
// export local element
297304
if (internalPath == null) {
298-
queuedExport.isForeign = false;
299-
queuedExport.referencedName = member.range.source.internalPath + PATH_DELIMITER + member.identifier.name;
305+
referencedName = member.range.source.internalPath + PATH_DELIMITER + member.identifier.name;
306+
307+
// resolve right away if the element exists
308+
if (this.elements.has(referencedName)) {
309+
this.exports.set(externalName, <Element>this.elements.get(referencedName));
310+
return;
311+
}
312+
313+
// otherwise queue it
314+
if (queuedExports.has(externalName)) {
315+
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, member.externalIdentifier.range, externalName);
316+
return;
317+
}
318+
const queuedExport: QueuedExport = new QueuedExport();
319+
queuedExport.isReExport = false;
320+
queuedExport.referencedName = referencedName; // -> internal name
321+
queuedExport.member = member;
322+
queuedExports.set(externalName, queuedExport);
323+
324+
// export external element
300325
} else {
301-
queuedExport.isForeign = true;
302-
queuedExport.referencedName = (<string>internalPath) + PATH_DELIMITER + member.identifier.name;
326+
referencedName = (<string>internalPath) + PATH_DELIMITER + member.externalIdentifier.name;
327+
328+
// resolve right away if the export exists
329+
if (this.exports.has(referencedName)) {
330+
this.exports.set(externalName, <Element>this.exports.get(referencedName));
331+
return;
332+
}
333+
334+
// walk already known queued exports
335+
const seen: Set<QueuedExport> = new Set();
336+
while (queuedExports.has(referencedName)) {
337+
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(referencedName);
338+
if (queuedExport.isReExport) {
339+
if (this.exports.has(queuedExport.referencedName)) {
340+
this.exports.set(externalName, <Element>this.exports.get(referencedName));
341+
return;
342+
}
343+
referencedName = queuedExport.referencedName;
344+
if (seen.has(queuedExport))
345+
break;
346+
seen.add(queuedExport);
347+
} else {
348+
if (this.elements.has(queuedExport.referencedName)) {
349+
this.exports.set(externalName, <Element>this.elements.get(referencedName));
350+
return;
351+
}
352+
break;
353+
}
354+
}
355+
356+
// otherwise queue it
357+
if (queuedExports.has(externalName)) {
358+
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, member.externalIdentifier.range, externalName);
359+
return;
360+
}
361+
const queuedReExport: QueuedExport = new QueuedExport();
362+
queuedReExport.isReExport = true;
363+
queuedReExport.referencedName = referencedName; // -> export name
364+
queuedReExport.member = member;
365+
queuedExports.set(externalName, queuedReExport);
303366
}
304-
queuedExport.member = member;
305-
queuedExports.set(exportName, queuedExport);
306367
}
307368

308369
private initializeFunction(declaration: FunctionDeclaration): void {
@@ -330,29 +391,48 @@ export class Program extends DiagnosticEmitter {
330391
}
331392

332393
private initializeImport(declaration: ImportDeclaration, internalPath: string, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void {
333-
const importName: string = internalPath + PATH_DELIMITER + declaration.externalIdentifier.name;
334-
let resolvedImportName: string = importName;
394+
const internalName: string = declaration.internalName;
395+
if (this.elements.has(internalName)) {
396+
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
397+
return;
398+
}
399+
400+
let referencedName: string = internalPath + PATH_DELIMITER + declaration.externalIdentifier.name;
401+
402+
// resolve right away if the export exists
403+
if (this.exports.has(referencedName)) {
404+
this.elements.set(internalName, <Element>this.exports.get(referencedName));
405+
return;
406+
}
407+
408+
// walk already known queued exports
335409
const seen: Set<QueuedExport> = new Set();
336-
while (queuedExports.has(resolvedImportName)) {
337-
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(resolvedImportName);
338-
resolvedImportName = queuedExport.referencedName;
339-
if (seen.has(queuedExport))
410+
while (queuedExports.has(referencedName)) {
411+
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(referencedName);
412+
if (queuedExport.isReExport) {
413+
if (this.exports.has(queuedExport.referencedName)) {
414+
this.elements.set(internalName, <Element>this.exports.get(referencedName));
415+
return;
416+
}
417+
referencedName = queuedExport.referencedName;
418+
if (seen.has(queuedExport))
419+
break;
420+
seen.add(queuedExport);
421+
} else {
422+
if (this.elements.has(queuedExport.referencedName)) {
423+
this.elements.set(internalName, <Element>this.elements.get(referencedName));
424+
return;
425+
}
340426
break;
341-
seen.add(queuedExport);
342-
}
343-
const internalName: string = declaration.internalName;
344-
if (this.exports.has(resolvedImportName)) { // resolvable right away
345-
if (this.elements.has(internalName))
346-
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
347-
else
348-
this.elements.set(internalName, <Element>this.exports.get(resolvedImportName));
349-
} else { // points to yet unresolved export
350-
const queuedImport: QueuedImport = new QueuedImport();
351-
queuedImport.internalName = internalName;
352-
queuedImport.importName = importName;
353-
queuedImport.declaration = declaration;
354-
queuedImports.push(queuedImport);
427+
}
355428
}
429+
430+
// otherwise queue it
431+
const queuedImport: QueuedImport = new QueuedImport();
432+
queuedImport.internalName = internalName;
433+
queuedImport.referencedName = referencedName;
434+
queuedImport.declaration = declaration;
435+
queuedImports.push(queuedImport);
356436
}
357437

358438
private initializeInterface(declaration: InterfaceDeclaration): void {

src/tokenizer.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -724,10 +724,9 @@ export class Tokenizer extends DiagnosticEmitter {
724724
range(start: i32 = -1, end: i32 = -1): Range {
725725
if (start < 0) {
726726
start = this.tokenPos;
727-
if (end < 0)
728-
end = start;
729-
} else if (end < 0)
730727
end = this.pos;
728+
} else if (end < 0)
729+
end = start;
731730
return new Range(this.source, start, end);
732731
}
733732

0 commit comments

Comments
 (0)