jsx
Version:
a faster, safer, easier JavaScript
911 lines (743 loc) • 28.9 kB
JSX
/*
* Copyright (c) 2012 DeNA Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import "./analysis.jsx";
import "./classdef.jsx";
import "./util.jsx";
import "./parser.jsx";
abstract class Type {
static const voidType = new VoidType();
static const nullType = new NullType();
static const booleanType = new BooleanType();
static const integerType = new IntegerType();
static const numberType = new NumberType();
static const stringType = new StringType();
static const variantType = new VariantType();
function serialize () : variant {
return this.toString();
}
abstract function isAssignable() : boolean;
abstract function isConvertibleTo(type : Type) : boolean;
abstract function getClassDef() : ClassDefinition;
abstract function instantiate (instantiationContext : InstantiationContext) : Type;
function equals (x : Type) : boolean {
return this == x;
}
function resolveIfNullable () : Type {
if (this instanceof NullableType)
return (this as NullableType).getBaseType();
return this;
}
function asAssignableType () : Type {
return this;
}
function toNullableType () : Type {
return this.toNullableType(false);
}
function toNullableType (force : boolean) : Type {
if (force || this instanceof PrimitiveType) {
return new NullableType(this);
}
return this;
}
static function templateTypeToString (parameterizedTypeName : string, typeArgs : Type[]) : string {
var s = parameterizedTypeName + ".<";
for (var i = 0; i < typeArgs.length; ++i) {
if (i != 0)
s += ",";
s += typeArgs[i].toString();
}
s += ">";
return s;
}
static function isIntegerOrNumber (type : Type) : boolean {
return type instanceof IntegerType || type instanceof NumberType;
}
static function calcLeastCommonAncestor (type1 : Type, type2 : Type) : Type {
return Type.calcLeastCommonAncestor(type1, type2, false);
}
static function calcLeastCommonAncestor (type1 : Type, type2 : Type, acceptVariant : boolean) : Type {
if (type1.equals(type2))
return type1;
if (Type.isIntegerOrNumber(type1) && Type.isIntegerOrNumber(type2))
return Type.numberType;
// VoidType
if (Type.voidType.equals(type1) || Type.voidType.equals(type2))
return null;
// VariantType
if (Type.variantType.equals(type1) || Type.variantType.equals(type2))
return Type.variantType;
// NullType
if (Type.nullType.equals(type1))
return (Type.nullType.isConvertibleTo(type2))? type2 : new NullableType(type2);
if (Type.nullType.equals(type2))
return (Type.nullType.isConvertibleTo(type1))? type1 : new NullableType(type1);
// PrimitiveTypes
if (type1.resolveIfNullable() instanceof PrimitiveType || type2.resolveIfNullable() instanceof PrimitiveType) {
if (type1.resolveIfNullable().equals(type2.resolveIfNullable()))
return new NullableType(type1);
else if (Type.isIntegerOrNumber(type1.resolveIfNullable()) && Type.isIntegerOrNumber(type2.resolveIfNullable()))
return new NullableType(Type.numberType);
else
return acceptVariant? Type.variantType : null;
}
// ObjectTypes
if (type1.resolveIfNullable() instanceof ObjectType && type2.resolveIfNullable() instanceof ObjectType) {
var obj1 = type1.resolveIfNullable() as ObjectType;
var obj2 = type2.resolveIfNullable() as ObjectType;
var ifaces1 = new ObjectType[];
for (;;) {
ifaces1 = ifaces1.concat(obj1.getClassDef().implementTypes().map.<ObjectType>((t) -> { return t; }));
if (obj2.isConvertibleTo(obj1))
break;
obj1 = obj1.getClassDef().extendType();
}
if (obj1.getClassDef().className() != "Object")
return obj1;
var candidates = new Type[];
for (var i in ifaces1) {
var iface = ifaces1[i];
do {
if (obj2.isConvertibleTo(iface)) {
candidates.push(iface);
break;
}
} while (iface = iface.getClassDef().extendType());
}
function uniquify(list : Type[]) : Type[] {
var result = new Type[];
for (var i = 0; i < list.length; ++i) {
result.push(list[i]);
for (var j = i + 1; j < list.length; ++j) {
if (list[i].equals(list[j])) {
result.pop();
break;
}
}
}
return result;
}
candidates = uniquify(candidates);
switch (candidates.length) {
case 0:
return obj1;
case 1:
return candidates[0];
default:
return null;
}
}
// FunctionType
if (type1.resolveIfNullable() instanceof FunctionType && type2.resolveIfNullable() instanceof FunctionType) {
return null;
}
// Otherwise
return acceptVariant? Type.variantType : null;
}
static function calcLeastCommonAncestor (types : Type[]) : Type {
return Type.calcLeastCommonAncestor(types, false);
}
static function calcLeastCommonAncestor (types : Type[], acceptVariant : boolean) : Type {
if (types.length == 0)
return null;
var type = types[0];
for (var i = 1; i < types.length; ++i) {
type = Type.calcLeastCommonAncestor(type, types[i], acceptVariant);
if (type == null)
return null;
}
return type;
}
}
// void and null are special types
class VoidType extends Type {
override function instantiate (instantiationContext : InstantiationContext) : VoidType {
return this;
}
override function isAssignable () : boolean {
return false;
}
override function isConvertibleTo (type : Type) : boolean {
return false;
}
override function getClassDef () : ClassDefinition {
throw new Error("VoidType#getClassDef() is not supported");
}
override function toString () : string {
return "void";
}
}
class NullType extends Type {
override function instantiate (instantiationContext : InstantiationContext) : NullType {
return this;
}
override function isAssignable () : boolean {
return false;
}
override function isConvertibleTo (type : Type) : boolean {
return type instanceof NullableType || type instanceof ObjectType || type instanceof VariantType || type instanceof StaticFunctionType;
}
override function getClassDef () : ClassDefinition {
throw new Error("NullType#getClassDef() is not supported");
}
override function toString () : string {
return "null";
}
}
// primitive types
abstract class PrimitiveType extends Type {
override function instantiate (instantiationContext : InstantiationContext) : Type {
return this;
}
override function isAssignable () : boolean {
return true; // still does not support "const" qualifier
}
}
class BooleanType extends PrimitiveType {
static var _classDef = null : ClassDefinition;
override function isConvertibleTo (type : Type) : boolean {
type = type.resolveIfNullable();
return type instanceof BooleanType || type instanceof VariantType;
}
override function getClassDef () : ClassDefinition {
return BooleanType._classDef;
}
override function toString () : string {
return "boolean";
}
}
class IntegerType extends PrimitiveType {
static var _classDef = null : ClassDefinition;
override function isConvertibleTo (type : Type) : boolean {
type = type.resolveIfNullable();
return type instanceof IntegerType || type instanceof NumberType || type instanceof VariantType;
}
override function getClassDef () : ClassDefinition {
return NumberType._classDef;
}
override function toString () : string {
return "int";
}
}
class NumberType extends PrimitiveType {
static var _classDef = null : ClassDefinition;
override function isConvertibleTo (type : Type) : boolean {
type = type.resolveIfNullable();
return type instanceof IntegerType || type instanceof NumberType || type instanceof VariantType;
}
override function getClassDef () : ClassDefinition {
return NumberType._classDef;
}
override function toString () : string {
return "number";
}
}
class StringType extends PrimitiveType {
static var _classDef = null : ClassDefinition;
override function isConvertibleTo (type : Type) : boolean {
type = type.resolveIfNullable();
return type instanceof StringType || type instanceof VariantType;
}
override function getClassDef () : ClassDefinition {
return StringType._classDef;
}
override function toString () : string {
return "string";
}
}
// any type
class VariantType extends Type {
override function instantiate (instantiationContext : InstantiationContext) : VariantType {
return this;
}
override function isAssignable () : boolean {
return true;
}
override function isConvertibleTo (type : Type) : boolean {
type = type.resolveIfNullable();
return type instanceof VariantType;
}
override function getClassDef () : ClassDefinition {
throw new Error("VariantType#getClassDef() is not supported");
}
override function toString () : string {
return "variant";
}
}
// Nullable
class NullableType extends Type {
var _baseType : Type;
function constructor (type : Type) {
if (type.equals(Type.variantType))
throw new Error("logic flaw, cannot create Nullable.<variant>");
this._baseType = type instanceof NullableType ? (type as NullableType)._baseType : type;
}
override function instantiate (instantiationContext : InstantiationContext) : Type {
var baseType = this._baseType.resolveIfNullable().instantiate(instantiationContext);
return baseType.toNullableType();
}
override function equals (x : Type) : boolean {
return x instanceof NullableType && this._baseType.equals((x as NullableType)._baseType);
}
override function isConvertibleTo (type : Type) : boolean {
return this._baseType.isConvertibleTo(type instanceof NullableType ? (type as NullableType)._baseType : type);
}
override function isAssignable () : boolean {
return true;
}
override function getClassDef () : ClassDefinition {
return this._baseType.getClassDef();
}
function getBaseType () : Type {
return this._baseType;
}
override function toString () : string {
return "Nullable.<" + this._baseType.toString() + ">";
}
}
class VariableLengthArgumentType extends Type {
var _baseType : Type;
function constructor (type : Type) {
if (type instanceof VariableLengthArgumentType)
throw new Error("logic flaw");
this._baseType = type;
}
override function instantiate (instantiationContext : InstantiationContext) : VariableLengthArgumentType {
var baseType = this._baseType.instantiate(instantiationContext);
return new VariableLengthArgumentType(baseType);
}
override function equals (x : Type) : boolean {
return x instanceof VariableLengthArgumentType && this._baseType.equals((x as VariableLengthArgumentType)._baseType);
}
override function isConvertibleTo (type : Type) : boolean {
throw new Error("logic flaw"); // never becomes LHS
}
override function isAssignable () : boolean {
throw new Error("logic flaw"); // never becomes LHS
}
override function getClassDef () : ClassDefinition {
throw new Error("logic flaw"); // never becomes LHS
}
function getBaseType () : Type {
return this._baseType;
}
override function toString () : string {
return "..." + this._baseType.toString();
}
}
// class and object types
class ObjectType extends Type {
var _classDef : ClassDefinition;
function constructor (classDef : ClassDefinition) {
this._classDef = classDef;
}
override function instantiate (instantiationContext : InstantiationContext) : Type {
throw new Error("logic flaw; ObjectType is created during semantic analysis, after template instantiation");
}
override function equals (x : Type) : boolean {
if (this instanceof ParsedObjectType && x instanceof ParsedObjectType
&& ((this as ParsedObjectType)._classDef == null || (x as ParsedObjectType)._classDef == null)) {
return this.toString() == x.toString();
}
return x instanceof ObjectType && this._classDef == (x as ObjectType)._classDef;
}
function resolveType (context : AnalysisContext) : void {
if (this._classDef == null)
throw new Error("logic flaw");
}
override function isConvertibleTo (type : Type) : boolean {
type = type.resolveIfNullable();
if (type instanceof VariantType)
return true;
// conversions from Number / String to number / string is handled in each operator (since the behavior differ bet. the operators)
if (! (type instanceof ObjectType)) {
return false;
}
if (this._classDef == null) { // occurs with completion mode
return false;
}
return this._classDef.isConvertibleTo((type as ObjectType)._classDef);
}
override function isAssignable () : boolean {
return true; // still does not support "const" qualifier
}
override function getClassDef () : ClassDefinition {
return this._classDef;
}
override function toString () : string {
return this._classDef != null ? this._classDef.className() : "(null)";
}
}
class ParsedObjectType extends ObjectType {
var _qualifiedName : QualifiedName;
var _typeArguments : Type[];
function constructor (qualifiedName : QualifiedName, typeArgs : Type[]) {
super(null);
this._qualifiedName = qualifiedName;
this._typeArguments = typeArgs;
}
function getToken () : Token {
return this._qualifiedName.getToken();
}
function getQualifiedName () : QualifiedName {
return this._qualifiedName;
}
function getTypeArguments () : Type[] {
return this._typeArguments;
}
override function instantiate (instantiationContext : InstantiationContext) : Type {
var enclosingType = this._qualifiedName.getEnclosingType();
if (enclosingType == null && this._typeArguments.length == 0) {
var actualType = instantiationContext.typemap[this._qualifiedName.getToken().getValue()];
if (actualType != null)
return actualType;
if (this._classDef == null)
instantiationContext.objectTypesUsed.push(this);
return this;
}
var qualifiedName = this._qualifiedName;
if (enclosingType != null) {
var actualEnclosingType = this._qualifiedName.getEnclosingType().instantiate(instantiationContext) as ParsedObjectType;
if (! this._qualifiedName.getEnclosingType().equals(actualEnclosingType)) {
qualifiedName = new QualifiedName(this._qualifiedName.getToken(), actualEnclosingType);
}
}
var typeArgs = new Type[];
for (var i = 0; i < this._typeArguments.length; ++i) {
if (this._typeArguments[i] instanceof ParsedObjectType && (this._typeArguments[i] as ParsedObjectType).getTypeArguments().length != 0) {
var actualType = this._typeArguments[i].instantiate(instantiationContext);
} else {
actualType = instantiationContext.typemap[this._typeArguments[i].toString()];
}
typeArgs[i] = actualType != null ? actualType : this._typeArguments[i];
// special handling for (Array|Map).<T> (T should not be NullableType)
if (typeArgs[i] instanceof NullableType) {
var templateClassName = qualifiedName.getToken().getValue();
if (templateClassName == "Array" || templateClassName == "Map") {
typeArgs[i] = (typeArgs[i] as NullableType).getBaseType();
}
}
}
var objectType = new ParsedObjectType(qualifiedName, typeArgs);
instantiationContext.objectTypesUsed.push(objectType);
return objectType;
}
override function resolveType (context : AnalysisContext) : void {
if (this._classDef == null) {
this._classDef = this._qualifiedName.getClass(context, this._typeArguments);
}
}
override function toString () : string {
return this._typeArguments.length != 0 ? Type.templateTypeToString(this._qualifiedName.getToken().getValue(), this._typeArguments) : this._qualifiedName.getToken().getValue();
}
}
// function types
abstract class FunctionType extends Type {
static var _classDef = null : ClassDefinition;
abstract function getObjectType() : Type;
// used for left to right deduction of callback function types and empty literals
abstract function getExpectedTypes (numberOfArgs : number, isStatic : boolean) : Type[][];
abstract function deduceByArgumentTypes (context : AnalysisContext, operatorToken : Token, argTypes : Type[], isStatic : boolean) : ResolvedFunctionType;
override function isConvertibleTo (type : Type) : boolean {
// functions except StaticFunctionType are unassignable
return false;
}
override function getClassDef () : ClassDefinition {
return FunctionType._classDef;
}
override function instantiate (instantiationContext : InstantiationContext) : FunctionType {
throw new Error("logic flaw");
}
}
class FunctionChoiceType extends FunctionType {
var _types : ResolvedFunctionType[];
function constructor (types : ResolvedFunctionType[]) {
this._types = types;
}
override function isAssignable () : boolean {
return false;
}
override function asAssignableType () : Type {
throw new Error("logic flaw");
}
override function deduceByArgumentTypes (context : AnalysisContext, operatorToken : Token, argTypes : Type[], isStatic : boolean) : ResolvedFunctionType {
var types = this._types;
// try an exact match
for (var i = 0; i < types.length; ++i) {
if (types[i]._deduceByArgumentTypes(types[i].getToken(), argTypes, isStatic, true, [])) {
return types[i];
}
}
// try loose match
var matched = new ResolvedFunctionType[];
var notes = new CompileNote[];
for (var i = 0; i < types.length; ++i) {
if (types[i]._deduceByArgumentTypes(types[i].getToken(), argTypes, isStatic, false, notes)) {
matched.push(types[i]);
}
}
switch (matched.length) {
case 0:
context.errors.push(
new CompileError(
operatorToken,
operatorToken.getValue() == "[" ? "operator [] of type " + argTypes[0].toString() + " is not applicable to " + this.getObjectType().toString() : "no function with matching arguments"));
break;
case 1:
return matched[0];
default:
context.errors.push(new CompileError(operatorToken, "result of function resolution using the arguments is ambiguous"));
break;
}
context.errors[context.errors.length - 1].addCompileNotes(notes);
return null;
}
override function getExpectedTypes (numberOfArgs : number, isStatic : boolean) : Type[][] {
var expected = new Type[][];
for (var i = 0; i < this._types.length; ++i)
this._types[i]._getExpectedTypes(expected, numberOfArgs, isStatic);
return expected;
}
override function toString () : string {
return this._types.length == 1 ? this._types[0].toString() : "<<multiple choices>>";
}
override function getObjectType () : Type {
throw new Error("logic flaw");
}
}
abstract class ResolvedFunctionType extends FunctionType {
var _token : Token;
var _returnType : Type;
var _argTypes : Type[];
var _isAssignable : boolean;
function constructor (token : Token, returnType : Type, argTypes : Type[], isAssignable : boolean) {
this._token = token;
this._returnType = returnType;
this._argTypes = argTypes;
this._isAssignable = isAssignable;
}
abstract function _clone () : ResolvedFunctionType;
abstract function _toStringPrefix() : string;
function setIsAssignable (isAssignable : boolean) : ResolvedFunctionType {
this._isAssignable = isAssignable;
return this;
}
override function isAssignable () : boolean {
return this._isAssignable;
}
override function asAssignableType () : Type {
return this._clone().setIsAssignable(true);
}
function getToken() : Token {
return this._token;
}
function getReturnType () : Type {
return this._returnType;
}
function getArgumentTypes () : Type[] {
return this._argTypes;
}
override function deduceByArgumentTypes (context : AnalysisContext, operatorToken : Token, argTypes : Type[], isStatic : boolean) : ResolvedFunctionType {
var notes = new CompileNote[];
if (! this._deduceByArgumentTypes(this._token != null ? this._token : operatorToken, argTypes, isStatic, false, notes)) {
var error = new CompileError(
operatorToken,
operatorToken.getValue() == "[" ? "operator [] of type " + argTypes[0].toString() + " is not applicable to " + this.getObjectType().toString() : "no function with matching arguments");
error.addCompileNotes(notes);
context.errors.push(error);
return null;
}
return this;
}
function _deduceByArgumentTypes (token : Token, argTypes : Type[], isStatic : boolean, exact : boolean, notes : CompileNote[]) : boolean {
var compareArg = function (formal : Type, actual : Type) : boolean {
if (formal.equals(actual))
return true;
else if (! exact && actual.isConvertibleTo(formal))
return true;
return false;
};
if ((this instanceof StaticFunctionType) != isStatic) {
if (isStatic) {
notes.push(new CompileNote(token, 'candidate function not viable: expected a static function, but got a member function'));
} else {
notes.push(new CompileNote(token, 'candidate function not viable: expected a member function, but got a static function'));
}
return false;
}
if (this._argTypes.length != 0 && this._argTypes[this._argTypes.length - 1] instanceof VariableLengthArgumentType) {
var vargType = this._argTypes[this._argTypes.length - 1] as VariableLengthArgumentType;
// a vararg function
if (argTypes.length < this._argTypes.length - 1) {
notes.push(new CompileNote(token, 'candidate function not viable: wrong number of arguments'));
return false;
}
for (var i = 0; i < this._argTypes.length - 1; ++i) {
if (! compareArg(this._argTypes[i], argTypes[i])) {
notes.push(new CompileNote(token, Util.format('candidate function not viable: no known conversion from %1 to %2 for %3 argument.', [ argTypes[i].toString(), this._argTypes[i].toString(), Util.toOrdinal(i+1) ])));
return false;
}
}
if (argTypes[i] instanceof VariableLengthArgumentType && argTypes.length == this._argTypes.length) {
if (! compareArg((this._argTypes[i] as VariableLengthArgumentType).getBaseType(), (argTypes[i] as VariableLengthArgumentType).getBaseType())) {
notes.push(new CompileNote(token, Util.format('candidate function not viable: no known conversion from %1 to %2 for %3 argument.', [ (argTypes[i] as VariableLengthArgumentType).getBaseType().toString(), (this._argTypes[i] as VariableLengthArgumentType).getBaseType().toString(), Util.toOrdinal(i+1) ])));
return false;
}
} else {
for (; i < argTypes.length; ++i) {
if (! compareArg(vargType.getBaseType(), argTypes[i])) {
notes.push(new CompileNote(token, Util.format('candidate function not viable: no known conversion from %1 to %2 for %3 argument.', [ argTypes[i].toString(), vargType.getBaseType().toString(), Util.toOrdinal(i+1) ])));
return false;
}
}
}
} else {
// non-vararg function
if (argTypes.length != this._argTypes.length) {
notes.push(new CompileNote(token, Util.format('candidate function not viable: wrong number of arguments (%1 for %2)',
[argTypes.length as string, this._argTypes.length as string])));
return false;
}
for (var i = 0; i < argTypes.length; ++i) {
if (! compareArg(this._argTypes[i], argTypes[i])) {
notes.push(new CompileNote(token, Util.format('candidate function not viable: no known conversion from %1 to %2 for %3 argument.', [ argTypes[i].toString(), this._argTypes[i].toString(), Util.toOrdinal(i+1) ])));
return false;
}
}
}
return true;
}
override function getExpectedTypes (numberOfArgs : number, isStatic : boolean) : Type[][] {
var expected = new Type[][];
this._getExpectedTypes(expected, numberOfArgs, isStatic);
return expected;
}
function _getExpectedTypes (expected : Type[][], numberOfArgs : number, isStatic : boolean) : void {
if ((this instanceof StaticFunctionType) != isStatic)
return;
var argTypes = new Type[];
if (this._argTypes.length > 0 && numberOfArgs >= this._argTypes.length
&& this._argTypes[this._argTypes.length - 1] instanceof VariableLengthArgumentType) {
for (var i = 0; i < numberOfArgs; ++i) {
if (i < this._argTypes.length - 1) {
argTypes[i] = this._argTypes[i];
} else {
argTypes[i] = (this._argTypes[this._argTypes.length - 1] as VariableLengthArgumentType).getBaseType();
}
}
} else if (this._argTypes.length == numberOfArgs) {
argTypes = this._argTypes;
} else {
// fail
return;
}
var hasCallback = false;
var callbackArgTypes = argTypes.map.<Type>(function (argType) {
var typeName = '';
if (argType instanceof StaticFunctionType
|| (argType instanceof ObjectType
&& argType.getClassDef() instanceof InstantiatedClassDefinition
&& ((typeName = (argType.getClassDef() as InstantiatedClassDefinition).getTemplateClassName()) == 'Array' || typeName == 'Map'))) {
hasCallback = true;
return argType;
} else {
return null;
}
});
if (hasCallback)
expected.push(callbackArgTypes);
}
override function toString () : string {
var args = new string[];
for (var i = 0; i < this._argTypes.length; ++i) {
if (this._argTypes[i] instanceof VariableLengthArgumentType) {
args[i] = "... : " + (this._argTypes[i] as VariableLengthArgumentType).getBaseType().toString();
} else {
args[i] = ": " + this._argTypes[i].toString();
}
}
return this._toStringPrefix() + "function (" + args.join(", ") + ") : " + this._returnType.toString();
}
override function getObjectType () : Type {
throw new Error("logic flaw");
}
}
class StaticFunctionType extends ResolvedFunctionType {
function constructor (token : Token, returnType : Type, argTypes : Type[], isAssignable : boolean) {
super(token, returnType, argTypes, isAssignable);
}
override function instantiate (instantiationContext : InstantiationContext) : StaticFunctionType {
var returnType = this._returnType.instantiate(instantiationContext);
if (returnType == null)
return null;
var argTypes = new Type[];
for (var i = 0; i < this._argTypes.length; ++i)
if ((argTypes[i] = this._argTypes[i].instantiate(instantiationContext)) == null)
return null;
return new StaticFunctionType(this._token, returnType, argTypes, this._isAssignable);
}
override function equals (x : Type) : boolean {
return x instanceof StaticFunctionType
&& this._returnType.equals((x as StaticFunctionType)._returnType)
&& Util.typesAreEqual(this._argTypes, (x as StaticFunctionType)._argTypes);
}
override function _clone () : ResolvedFunctionType {
return new StaticFunctionType(this._token, this._returnType, this._argTypes, this._isAssignable);
}
override function isConvertibleTo (type : Type) : boolean {
type = type.resolveIfNullable();
if (type instanceof VariantType)
return true;
if (! (type instanceof StaticFunctionType))
return false;
if (! this._returnType.equals((type as StaticFunctionType).getReturnType()))
return false;
return this._deduceByArgumentTypes((type as ResolvedFunctionType).getToken(), (type as StaticFunctionType).getArgumentTypes(), true, true, []);
}
override function _toStringPrefix () : string {
return "";
}
override function getObjectType () : Type {
throw new Error("logic flaw");
}
}
class MemberFunctionType extends ResolvedFunctionType {
var _objectType : Type;
function constructor (token : Token, objectType : Type, returnType : Type, argTypes : Type[], isAssignable : boolean) {
super(token, returnType, argTypes, isAssignable);
this._objectType = objectType;
}
override function equals (x : Type) : boolean {
return x instanceof MemberFunctionType
&& this._objectType == (x as MemberFunctionType)._objectType
&& this._returnType.equals((x as MemberFunctionType)._returnType)
&& Util.typesAreEqual(this._argTypes, (x as MemberFunctionType)._argTypes);
}
override function _clone () : MemberFunctionType {
return new MemberFunctionType(this._token, this._objectType, this._returnType, this._argTypes, this._isAssignable);
}
override function _toStringPrefix () : string {
return this._objectType.toString() + ".";
}
override function getObjectType () : Type {
return this._objectType;
}
}