Buggy python submission (verdict: Time Limit Exceeded):
#!/usr/bin/env python3

import sys
import heapq
import copy
import itertools

inf = float('inf')
a = '.'
b = '#'

class Graph_dijkstra(object):
    def __init__(self, V: "List[int]", E: "Set[Tuple[Tuple, Tuple]]", L: "Dict[Tuple[Tuple, Tuple], int]"):
        self.V = V
        self.E = E
        self.L = L
        self.adjacent = self.init_adjacent() # type: Dict[int: List[int]]
        self.shortest_path = self.init_shortest_path() # type: Dict[FrozenSet[int, int], int]
    
    def init_adjacent(self):
        ret = {v: [] for v in self.V}
        for e in self.E:
            ret[e[0]].append(e[1])
        return ret
    
    def init_shortest_path(self):
        ret = {(s, g): inf for s, g in itertools.permutations(self.V, 2)}
        for v in self.V:
            ret[(v, v)] = 0
        return ret
    
    def dijkstra(self, s: int):
        D = {v: inf for v in self.V}
        D[s] = 0
        prev = {v: None for v in self.V}
        Q = [(0, s)]

        while len(Q) > 0:
            du, u = heapq.heappop(Q)
            for v in self.adjacent[u]:
                e = (u, v)
                alt = du + self.L[e]
                if alt < D[v]:
                    D[v] = alt
                    prev[v] = u
                    heapq.heappush(Q, (D[v], v))

        for v in self.V:
            self.shortest_path[(s, v)] = D[v]

    def find_shortest_path(self, s: int, g:int):
        d = self.shortest_path[(s, g)]
        if d < inf:
            return d
        self.dijkstra(s)
        return self.shortest_path[(s, g)]


def solve(H, W, s):
    V = []
    E = set()
    L = dict()
    direction = [(1, 0), (0, 1), (-1, 0), (0, -1)]
    field = [[b for w in range(W+2)] for h in range(H+2)]
    white_count = 0
    for h in range(H):
        field[1+h][1:W+1] = s[h]
    for h in range(H):
        for w in range(W):
            if field[1+h][1+w] == b:
                continue
            V.append((1+h, 1+w))
            white_count += 1
            for d in direction:
                if field[1+h+d[0]][1+w+d[1]] == b:
                    continue
                e = ((1+h, 1+w), (1+h+d[0], 1+w+d[1]))
                E.add(e)
                L[e] = 1
    G = Graph_dijkstra(V, E, L)
    path_length = G.find_shortest_path((1, 1), (H, W))
    if path_length == inf:
        print(-1)
    else:
        print(white_count - (path_length + 1))
    return

# Generated by 1.1.4 https://github.com/kyuridenamida/atcoder-tools  (tips: You use the default template now. You can remove this line by using your custom template)
def main():
    def iterate_tokens():
        for line in sys.stdin:
            for word in line.split():
                yield word
    tokens = iterate_tokens()
    H = int(next(tokens))  # type: int
    W = int(next(tokens))  # type: int
    s = [[c for c in next(tokens)] for _ in range(H)]  # type: "List[str]"
    solve(H, W, s)
    pass

if __name__ == '__main__':
    main()


Fixed submission:
#!/usr/bin/env python3

import sys
import heapq
import copy
import itertools

inf = float('inf')
a = '.'
b = '#'

def solve(H, W, s):
    white_count = sum([line.count(a) for line in s])
    
    field = [[b for w in range(W+2)] for h in range(H+2)]
    for h in range(H):
        field[1+h][1:W+1] = s[h]

    D = {(h, w): inf for h, w in itertools.product(range(H+2), range(W+2))}
    start = (1, 1)
    D[start] = 1
    Q = [(D[start], start)]
    direction = [(1, 0), (0, 1), (-1, 0), (0, -1)]
    
    while len(Q) > 0:
        Du, u = heapq.heappop(Q)
        for d in direction:
            v = (u[0]+d[0], u[1]+d[1])
            if field[u[0]+d[0]][u[1]+d[1]] == b:
                continue
            if Du + 1 < D[v]:
                D[v] = Du + 1
                heapq.heappush(Q, (D[v], v))

    path_length = D[(H, W)]
    if path_length == inf:
        print(-1)
        return
    print(white_count - path_length)
    return

# Generated by 1.1.4 https://github.com/kyuridenamida/atcoder-tools  (tips: You use the default template now. You can remove this line by using your custom template)
def main():
    def iterate_tokens():
        for line in sys.stdin:
            for word in line.split():
                yield word
    tokens = iterate_tokens()
    H = int(next(tokens))  # type: int
    W = int(next(tokens))  # type: int
    s = [[c for c in next(tokens)] for _ in range(H)]  # type: "List[str]"
    solve(H, W, s)
    pass

if __name__ == '__main__':
    main()