/** Builds the function type, and puts it in the registry. */ FunctionType buildAndRegister() { if (returnType == null) { returnType = typeRegistry.getNativeType(UNKNOWN_TYPE); } if (parametersNode == null) { throw new IllegalStateException("All Function types must have params and a return type"); } FunctionType fnType; if (isConstructor) { fnType = getOrCreateConstructor(); } else if (isInterface) { fnType = typeRegistry.createInterfaceType(fnName, sourceNode); if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) { typeRegistry.declareType(fnName, fnType.getInstanceType()); } maybeSetBaseType(fnType); } else { fnType = new FunctionBuilder(typeRegistry) .withName(fnName) .withSourceNode(sourceNode) .withParamsNode(parametersNode) .withReturnType(returnType, returnTypeInferred) .withTypeOfThis(thisType) .withTemplateName(templateTypeName) .build(); maybeSetBaseType(fnType); } if (implementedInterfaces != null) { fnType.setImplementedInterfaces(implementedInterfaces); } typeRegistry.clearTemplateTypeName(); return fnType; }
/** * Returns a constructor function either by returning it from the registry if it exists or * creating and registering a new type. If there is already a type, then warn if the existing type * is different than the one we are creating, though still return the existing function if * possible. The primary purpose of this is that registering a constructor will fail for all * built-in types that are initialized in {@link JSTypeRegistry}. We a) want to make sure that the * type information specified in the externs file matches what is in the registry and b) annotate * the externs with the {@link JSType} from the registry so that there are not two separate JSType * objects for one type. */ private FunctionType getOrCreateConstructor() { FunctionType fnType = typeRegistry.createConstructorType(fnName, sourceNode, parametersNode, returnType); JSType existingType = typeRegistry.getType(fnName); if (existingType != null) { boolean isInstanceObject = existingType instanceof InstanceObjectType; if (isInstanceObject || fnName.equals("Function")) { FunctionType existingFn = isInstanceObject ? ((InstanceObjectType) existingType).getConstructor() : typeRegistry.getNativeFunctionType(FUNCTION_FUNCTION_TYPE); if (existingFn.getSource() == null) { existingFn.setSource(sourceNode); } if (!existingFn.hasEqualCallType(fnType)) { reportWarning(TYPE_REDEFINITION, fnName, fnType.toString(), existingFn.toString()); } return existingFn; } else { // We fall through and return the created type, even though it will fail // to register. We have no choice as we have to return a function. We // issue an error elsewhere though, so the user should fix it. } } maybeSetBaseType(fnType); if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) { typeRegistry.declareType(fnName, fnType.getInstanceType()); } return fnType; }