Support 'this' in static functions, fixes #45; Fix propagation of 'ambient' flag

This commit is contained in:
dcodeIO
2018-03-20 12:02:05 +01:00
parent fea8e65a41
commit 2c0ddf4f80
12 changed files with 163 additions and 86 deletions

View File

@ -2457,6 +2457,8 @@ function evaluateConstantOffset(compiler: Compiler, expression: Expression): i32
return value;
}
const allocateInternalName = "allocate_memory";
/** Compiles a memory allocation for an instance of the specified class. */
export function compileAllocate(
compiler: Compiler,
@ -2468,11 +2470,11 @@ export function compileAllocate(
var module = compiler.module;
var options = compiler.options;
var prototype = program.elementsLookup.get(options.allocateImpl);
var prototype = program.elementsLookup.get(allocateInternalName);
if (!prototype) {
program.error(
DiagnosticCode.Cannot_find_name_0,
reportNode.range, options.allocateImpl
reportNode.range, allocateInternalName
);
return module.createUnreachable();
}
@ -2498,6 +2500,8 @@ export function compileAllocate(
);
}
const abortInternalName = "abort";
/** Compiles an abort wired to the conditionally imported 'abort' function. */
export function compileAbort(
compiler: Compiler,
@ -2510,7 +2514,7 @@ export function compileAbort(
var stringType = program.typesLookup.get("string"); // might be intended
if (!stringType) return module.createUnreachable();
var abortPrototype = program.elementsLookup.get("abort"); // might be intended
var abortPrototype = program.elementsLookup.get(abortInternalName); // might be intended
if (!abortPrototype || abortPrototype.kind != ElementKind.FUNCTION_PROTOTYPE) return module.createUnreachable();
var abortInstance = (<FunctionPrototype>abortPrototype).resolve(); // reports

View File

@ -142,10 +142,6 @@ export class Options {
importMemory: bool = false;
/** Static memory start offset. */
memoryBase: u32 = 0;
/** Memory allocation implementation to use. */
allocateImpl: string = "allocate_memory";
/** Memory freeing implementation to use. */
freeImpl: string = "free_memory";
/** If true, generates information necessary for source maps. */
sourceMap: bool = false;
@ -513,7 +509,7 @@ export class Compiler extends DiagnosticEmitter {
var isConstant = global.isAny(CommonFlags.CONST) || global.is(CommonFlags.STATIC | CommonFlags.READONLY);
// handle imports
if (global.is(CommonFlags.DECLARE)) {
if (global.is(CommonFlags.AMBIENT)) {
// constant global
if (isConstant) {
@ -818,14 +814,14 @@ export class Compiler extends DiagnosticEmitter {
var declaration = instance.prototype.declaration;
var body = declaration.body;
if (body) {
if (instance.is(CommonFlags.DECLARE)) {
if (instance.is(CommonFlags.AMBIENT)) {
this.error(
DiagnosticCode.An_implementation_cannot_be_declared_in_ambient_contexts,
declaration.name.range
);
}
} else {
if (!instance.is(CommonFlags.DECLARE)) {
if (!instance.is(CommonFlags.AMBIENT)) {
this.error(
DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration,
declaration.name.range
@ -4231,7 +4227,7 @@ export class Compiler extends DiagnosticEmitter {
var trampolineSignature = new Signature(trampolineParameterTypes, commonReturnType, commonThisType);
var trampolineName = originalName + "|trampoline";
trampolineSignature.requiredParameters = maxArguments + 1;
trampoline = new Function(original.prototype, trampolineName, trampolineSignature, original.instanceMethodOf);
trampoline = new Function(original.prototype, trampolineName, trampolineSignature, original.memberOf);
trampoline.flags = original.flags;
trampoline.set(CommonFlags.COMPILED);
original.trampoline = trampoline;
@ -4463,7 +4459,9 @@ export class Compiler extends DiagnosticEmitter {
case NodeKind.THIS: {
let currentFunction = this.currentFunction;
if (currentFunction.is(CommonFlags.INSTANCE)) {
let thisType = assert(currentFunction.instanceMethodOf).type;
let parent = assert(currentFunction.memberOf);
assert(parent.kind == ElementKind.CLASS);
let thisType = (<Class>parent).type;
this.currentType = thisType;
return module.createGetLocal(0, thisType.toNativeType());
}
@ -4477,7 +4475,9 @@ export class Compiler extends DiagnosticEmitter {
case NodeKind.SUPER: {
let currentFunction = this.currentFunction;
if (currentFunction.is(CommonFlags.INSTANCE)) {
let base = assert(currentFunction.instanceMethodOf).base;
let parent = assert(currentFunction.memberOf);
assert(parent.kind == ElementKind.CLASS);
let base = (<Class>parent).base;
if (base) {
let superType = base.type;
this.currentType = superType;
@ -4971,10 +4971,12 @@ export class Compiler extends DiagnosticEmitter {
)) {
return module.createUnreachable();
}
if (instance.instanceMethodOf) {
if (instance.is(CommonFlags.INSTANCE)) {
let parent = assert(instance.memberOf);
assert(parent.kind == ElementKind.CLASS);
targetExpr = this.compileExpression(
<Expression>resolved.targetExpression,
instance.instanceMethodOf.type
(<Class>parent).type
);
this.currentType = signature.returnType;
return this.compileCallDirect(instance, [], propertyAccess, targetExpr);

View File

@ -87,10 +87,7 @@ export class Parser extends DiagnosticEmitter {
backlog: string[] = new Array();
/** Log of source file names already processed. */
seenlog: Set<string> = new Set();
currentDeclareStart: i32 = 0;
currentDeclareEnd: i32 = 0;
/** Optional handler to intercept comments while tokenizing. */
onComment: CommentHandler | null = null;
/** Constructs a new parser. */
@ -145,7 +142,7 @@ export class Parser extends DiagnosticEmitter {
/** Parses a top-level statement. */
parseTopLevelStatement(
tn: Tokenizer,
isNamespaceMember: bool = false
namespace: Node | null = null
): Statement | null {
var flags = CommonFlags.NONE;
var startPos: i32 = -1;
@ -188,11 +185,18 @@ export class Parser extends DiagnosticEmitter {
var declareStart: i32 = 0;
var declareEnd: i32 = 0;
var contextIsAmbient = namespace != null && namespace.is(CommonFlags.AMBIENT);
if (tn.skip(Token.DECLARE)) {
if (startPos < 0) startPos = tn.tokenPos;
flags |= CommonFlags.DECLARE;
this.currentDeclareStart = declareStart = tn.tokenPos;
this.currentDeclareEnd = declareEnd = tn.pos;
if (contextIsAmbient) {
this.error(
DiagnosticCode.A_declare_modifier_cannot_be_used_in_an_already_ambient_context,
tn.range()
); // recoverable
}
flags |= CommonFlags.DECLARE | CommonFlags.AMBIENT;
} else if (contextIsAmbient) {
flags |= CommonFlags.AMBIENT;
}
// parse the statement
@ -293,9 +297,9 @@ export class Parser extends DiagnosticEmitter {
tn.range(declareStart, declareEnd), "declare"
); // recoverable
}
if (!isNamespaceMember) {
if (!namespace) {
statement = this.parseStatement(tn, true);
}
} // TODO: else?
}
break;
}
@ -761,7 +765,7 @@ export class Parser extends DiagnosticEmitter {
if (!initializer) return null;
} else {
if (flags & CommonFlags.CONST) {
if (!(flags & CommonFlags.DECLARE)) {
if (!(flags & CommonFlags.AMBIENT)) {
this.error(
DiagnosticCode._const_declarations_must_be_initialized,
identifier.range
@ -1173,17 +1177,6 @@ export class Parser extends DiagnosticEmitter {
tn.range(signatureStart, tn.pos)
);
if (flags & CommonFlags.DECLARE) {
if (flags & CommonFlags.AMBIENT) {
this.error(
DiagnosticCode.A_declare_modifier_cannot_be_used_in_an_already_ambient_context,
tn.range(this.currentDeclareStart, this.currentDeclareEnd)
); // recoverable
} else {
flags |= CommonFlags.AMBIENT;
}
}
var body: Statement | null = null;
if (tn.skip(Token.OPENBRACE)) {
if (flags & CommonFlags.AMBIENT) {
@ -1393,17 +1386,6 @@ export class Parser extends DiagnosticEmitter {
return null;
}
if (flags & CommonFlags.DECLARE) {
if (flags & CommonFlags.AMBIENT) {
this.error(
DiagnosticCode.A_declare_modifier_cannot_be_used_in_an_already_ambient_context,
tn.range(this.currentDeclareStart, this.currentDeclareEnd)
); // recoverable
} else {
flags |= CommonFlags.AMBIENT;
}
}
var members = new Array<DeclarationStatement>();
if (!tn.skip(Token.CLOSEBRACE)) {
do {
@ -1744,20 +1726,21 @@ export class Parser extends DiagnosticEmitter {
let identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
if (tn.skip(Token.OPENBRACE)) {
let members = new Array<Statement>();
while (!tn.skip(Token.CLOSEBRACE)) {
let member = this.parseTopLevelStatement(tn, true);
if (!member) return null;
members.push(member);
}
let ret = Node.createNamespaceDeclaration(
let ns = Node.createNamespaceDeclaration(
identifier,
members,
decorators,
flags,
tn.range(startPos, tn.pos)
);
while (!tn.skip(Token.CLOSEBRACE)) {
let member = this.parseTopLevelStatement(tn, ns);
if (!member) return null;
member.parent = ns;
members.push(member);
}
tn.skip(Token.SEMICOLON);
return ret;
return ns;
} else {
this.error(
DiagnosticCode._0_expected,

View File

@ -579,7 +579,7 @@ export class Program extends DiagnosticEmitter {
simpleName,
internalName,
declaration,
null
classPrototype
);
classPrototype.members.set(simpleName, prototype);
this.elementsLookup.set(internalName, prototype);
@ -1748,7 +1748,7 @@ export class Program extends DiagnosticEmitter {
} else {
break;
}
// or inherited instance members on the cbase class while target is a class instance
// or inherited instance members on the base class while target is a class instance
} else if (target.kind == ElementKind.CLASS) {
if ((<Class>target).base) {
target = <Class>(<Class>target).base;
@ -1836,11 +1836,11 @@ export class Program extends DiagnosticEmitter {
case NodeKind.BINARY: { // TODO: string concatenation, mostly
throw new Error("not implemented");
}
case NodeKind.THIS: { // -> Class
let classType = contextualFunction.instanceMethodOf;
if (classType) {
case NodeKind.THIS: { // -> Class / ClassPrototype
let parent = contextualFunction.memberOf;
if (parent) {
if (!resolvedElement) resolvedElement = new ResolvedElement();
return resolvedElement.set(classType);
return resolvedElement.set(parent);
}
this.error(
DiagnosticCode._this_cannot_be_referenced_in_current_location,
@ -1849,10 +1849,10 @@ export class Program extends DiagnosticEmitter {
return null;
}
case NodeKind.SUPER: { // -> Class
let classType = contextualFunction.instanceMethodOf;
if (classType && (classType = classType.base)) {
let parent = contextualFunction.memberOf;
if (parent && parent.kind == ElementKind.CLASS && (parent = (<Class>parent).base)) {
if (!resolvedElement) resolvedElement = new ResolvedElement();
return resolvedElement.set(classType);
return resolvedElement.set(parent);
}
this.error(
DiagnosticCode._super_can_only_be_referenced_in_a_derived_class,
@ -2294,6 +2294,7 @@ export class FunctionPrototype extends Element {
var declaration = this.declaration;
var isInstance = this.is(CommonFlags.INSTANCE);
var classPrototype = this.classPrototype;
// inherit contextual type arguments as provided. might be be overridden.
var inheritedTypeArguments = contextualTypeArguments;
@ -2310,7 +2311,8 @@ export class FunctionPrototype extends Element {
// override with class type arguments if a partially resolved instance method
var classTypeArguments = this.classTypeArguments;
if (classTypeArguments) { // set only if partially resolved
let classDeclaration = (<ClassPrototype>assert(this.classPrototype)).declaration;
assert(this.is(CommonFlags.INSTANCE));
let classDeclaration = assert(classPrototype).declaration;
let classTypeParameters = classDeclaration.typeParameters;
let numClassTypeParameters = classTypeParameters.length;
assert(numClassTypeParameters == classTypeArguments.length);
@ -2344,12 +2346,9 @@ export class FunctionPrototype extends Element {
var classInstance: Class | null = null;
var thisType: Type | null = null;
if (isInstance) {
let classPrototype = assert(this.classPrototype);
classInstance = classPrototype.resolve(classTypeArguments, contextualTypeArguments); // reports
classInstance = assert(classPrototype).resolve(classTypeArguments, contextualTypeArguments); // reports
if (!classInstance) return null;
thisType = classInstance.type;
} else {
assert(!this.classPrototype);
}
// resolve signature node
@ -2386,7 +2385,7 @@ export class FunctionPrototype extends Element {
var internalName = this.internalName;
if (instanceKey.length) internalName += "<" + instanceKey + ">";
instance = new Function(this, internalName, signature, classInstance);
instance = new Function(this, internalName, signature, classInstance ? classInstance : classPrototype);
instance.contextualTypeArguments = contextualTypeArguments;
this.instances.set(instanceKey, instance);
return instance;
@ -2394,6 +2393,7 @@ export class FunctionPrototype extends Element {
/** Resolves this prototype partially by applying the specified inherited class type arguments. */
resolvePartial(classTypeArguments: Type[] | null): FunctionPrototype | null {
assert(this.is(CommonFlags.INSTANCE));
assert(this.classPrototype);
if (classTypeArguments && classTypeArguments.length) {
let partialPrototype = new FunctionPrototype(
@ -2465,8 +2465,8 @@ export class Function extends Element {
prototype: FunctionPrototype;
/** Function signature. */
signature: Signature;
/** If an instance method, the concrete class it is a member of. */
instanceMethodOf: Class | null;
/** If a member of another namespace-like element, the concrete element it is a member of. */
memberOf: Element | null;
/** Map of locals by name. */
locals: Map<string,Local> = new Map();
/** List of additional non-parameter locals. */
@ -2494,16 +2494,16 @@ export class Function extends Element {
prototype: FunctionPrototype,
internalName: string,
signature: Signature,
instanceMethodOf: Class | null = null
memberOf: Element | null = null
) {
super(prototype.program, prototype.simpleName, internalName);
this.prototype = prototype;
this.signature = signature;
this.instanceMethodOf = instanceMethodOf;
this.memberOf = memberOf;
this.flags = prototype.flags;
if (!(prototype.is(CommonFlags.BUILTIN) || prototype.is(CommonFlags.DECLARE))) {
let localIndex = 0;
if (instanceMethodOf) {
if (memberOf && memberOf.kind == ElementKind.CLASS) {
assert(this.is(CommonFlags.INSTANCE));
this.locals.set(
"this",
@ -2514,11 +2514,12 @@ export class Function extends Element {
assert(signature.thisType)
)
);
if (instanceMethodOf.contextualTypeArguments) {
let contextualTypeArguments = (<Class>memberOf).contextualTypeArguments;
if (contextualTypeArguments) {
if (!this.contextualTypeArguments) {
this.contextualTypeArguments = new Map();
}
for (let [inheritedName, inheritedType] of instanceMethodOf.contextualTypeArguments) {
for (let [inheritedName, inheritedType] of contextualTypeArguments) {
this.contextualTypeArguments.set(inheritedName, inheritedType);
}
}